summaryrefslogtreecommitdiff
path: root/button.c
diff options
context:
space:
mode:
Diffstat (limited to 'button.c')
-rw-r--r--button.c4929
1 files changed, 4929 insertions, 0 deletions
diff --git a/button.c b/button.c
new file mode 100644
index 0000000..cdf05b3
--- /dev/null
+++ b/button.c
@@ -0,0 +1,4929 @@
+/* $XTermId: button.c,v 1.434 2012/05/10 11:06:10 tom Exp $ */
+
+/*
+ * Copyright 1999-2011,2012 by Thomas E. Dickey
+ *
+ * All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name(s) of the above copyright
+ * holders shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization.
+ *
+ *
+ * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital Equipment
+ * Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ *
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+button.c Handles button events in the terminal emulator.
+ does cut/paste operations, change modes via menu,
+ passes button events through to some applications.
+ J. Gettys.
+*/
+
+#include <xterm.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+#include <X11/Xmu/StdSel.h>
+
+#include <xutf8.h>
+#include <fontutils.h>
+
+#include <data.h>
+#include <error.h>
+#include <menu.h>
+#include <xcharmouse.h>
+#include <charclass.h>
+#include <xstrings.h>
+
+#if OPT_SELECT_REGEX
+#ifdef HAVE_PCREPOSIX_H
+#include <pcreposix.h>
+#else /* POSIX regex.h */
+#include <sys/types.h>
+#include <regex.h>
+#endif
+#endif
+
+#if OPT_WIDE_CHARS
+#include <ctype.h>
+#include <wcwidth.h>
+#else
+#define CharacterClass(value) \
+ charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
+#endif
+
+ /*
+ * We'll generally map rows to indices when doing selection.
+ * Simplify that with a macro.
+ *
+ * Note that ROW2INX() is safe to use with auto increment/decrement for
+ * the row expression since that is evaluated once.
+ */
+#define GET_LINEDATA(screen, row) \
+ getLineData(screen, ROW2INX(screen, row))
+
+ /*
+ * We reserve shift modifier for cut/paste operations. In principle we
+ * can pass through control and meta modifiers, but in practice, the
+ * popup menu uses control, and the window manager is likely to use meta,
+ * so those events are not delivered to SendMousePosition.
+ */
+#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
+#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
+ Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
+
+#define BtnModifiers(event) (event->state & OurModifiers)
+#define KeyModifiers(event) (event->xbutton.state & OurModifiers)
+
+#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
+#define IsKeyEvent(event) ((event)->type == KeyPress || (event)->type == KeyRelease)
+
+#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
+ + (((x) & Mod1Mask) ? 2 : 0))
+ /* adds together the bits:
+ shift key -> 1
+ meta key -> 2
+ control key -> 4 */
+
+#define Coordinate(s,c) ((c)->row * MaxCols(s) + (c)->col)
+
+static const CELL zeroCELL =
+{0, 0};
+
+#if OPT_DEC_LOCATOR
+static Bool SendLocatorPosition(XtermWidget xw, XButtonEvent * event);
+static void CheckLocatorPosition(XtermWidget xw, XButtonEvent * event);
+#endif /* OPT_DEC_LOCATOR */
+
+/* Multi-click handling */
+#if OPT_READLINE
+static Time lastButtonDownTime = 0;
+static int ExtendingSelection = 0;
+static Time lastButton3UpTime = 0;
+static Time lastButton3DoubleDownTime = 0;
+static CELL lastButton3; /* At the release time */
+#endif /* OPT_READLINE */
+
+static Char *SaveText(TScreen * screen, int row, int scol, int ecol,
+ Char * lp, int *eol);
+static int Length(TScreen * screen, int row, int scol, int ecol);
+static void ComputeSelect(XtermWidget xw, CELL * startc, CELL * endc, Bool extend);
+static void EditorButton(XtermWidget xw, XButtonEvent * event);
+static void EndExtend(XtermWidget w, XEvent * event, String * params, Cardinal
+ num_params, Bool use_cursor_loc);
+static void ExtendExtend(XtermWidget xw, const CELL * cell);
+static void PointToCELL(TScreen * screen, int y, int x, CELL * cell);
+static void ReHiliteText(XtermWidget xw, CELL * first, CELL * last);
+static void SaltTextAway(XtermWidget xw, CELL * cellc, CELL * cell);
+static void SelectSet(XtermWidget xw, XEvent * event, String * params, Cardinal num_params);
+static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
+static void StartSelect(XtermWidget xw, const CELL * cell);
+static void TrackDown(XtermWidget xw, XButtonEvent * event);
+static void TrackText(XtermWidget xw, const CELL * first, const CELL * last);
+static void _OwnSelection(XtermWidget xw, String * selections, Cardinal count);
+static void do_select_end(XtermWidget xw, XEvent * event, String * params,
+ Cardinal *num_params, Bool use_cursor_loc);
+
+#define MOUSE_LIMIT (255 - 32)
+
+/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
+#define EXT_MOUSE_LIMIT (2047 - 32)
+#define EXT_MOUSE_START (127 - 32)
+
+static int
+MouseLimit(TScreen * screen)
+{
+ int mouse_limit;
+
+ switch (screen->extend_coords) {
+ default:
+ mouse_limit = MOUSE_LIMIT;
+ break;
+ case SET_EXT_MODE_MOUSE:
+ mouse_limit = EXT_MOUSE_LIMIT;
+ break;
+ case SET_SGR_EXT_MODE_MOUSE:
+ case SET_URXVT_EXT_MODE_MOUSE:
+ mouse_limit = -1;
+ break;
+ }
+ return mouse_limit;
+}
+
+static unsigned
+EmitMousePosition(TScreen * screen, Char line[], unsigned count, int value)
+{
+ int mouse_limit = MouseLimit(screen);
+
+ /*
+ * Add pointer position to key sequence
+ *
+ * In extended mode we encode large positions as two-byte UTF-8.
+ *
+ * NOTE: historically, it was possible to emit 256, which became
+ * zero by truncation to 8 bits. While this was arguably a bug,
+ * it's also somewhat useful as a past-end marker. We preserve
+ * this behavior for both normal and extended mouse modes.
+ */
+ switch (screen->extend_coords) {
+ default:
+ if (value == mouse_limit) {
+ line[count++] = CharOf(0);
+ } else {
+ line[count++] = CharOf(' ' + value + 1);
+ }
+ break;
+ case SET_EXT_MODE_MOUSE:
+ if (value == mouse_limit) {
+ line[count++] = CharOf(0);
+ } else if (value < EXT_MOUSE_START) {
+ line[count++] = CharOf(' ' + value + 1);
+ } else {
+ value += ' ' + 1;
+ line[count++] = CharOf(0xC0 + (value >> 6));
+ line[count++] = CharOf(0x80 + (value & 0x3F));
+ }
+ break;
+ case SET_SGR_EXT_MODE_MOUSE:
+ /* FALLTHRU */
+ case SET_URXVT_EXT_MODE_MOUSE:
+ count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
+ break;
+ }
+ return count;
+}
+
+static unsigned
+EmitMousePositionSeparator(TScreen * screen, Char line[], unsigned count)
+{
+ switch (screen->extend_coords) {
+ case SET_SGR_EXT_MODE_MOUSE:
+ case SET_URXVT_EXT_MODE_MOUSE:
+ line[count++] = ';';
+ break;
+ }
+ return count;
+}
+
+Bool
+SendMousePosition(XtermWidget xw, XEvent * event)
+{
+ TScreen *screen = TScreenOf(xw);
+ XButtonEvent *my_event = (XButtonEvent *) event;
+ Bool result = False;
+
+ switch (screen->send_mouse_pos) {
+ case MOUSE_OFF:
+ /* If send_mouse_pos mode isn't on, we shouldn't be here */
+ break;
+
+ case BTN_EVENT_MOUSE:
+ case ANY_EVENT_MOUSE:
+ if (KeyModifiers(event) == 0 || KeyModifiers(event) == ControlMask) {
+ /* xterm extension for motion reporting. June 1998 */
+ /* EditorButton() will distinguish between the modes */
+ switch (event->type) {
+ case MotionNotify:
+ my_event->button = 0;
+ /* FALLTHRU */
+ case ButtonPress:
+ /* FALLTHRU */
+ case ButtonRelease:
+ EditorButton(xw, my_event);
+ result = True;
+ break;
+ }
+ }
+ break;
+
+ default:
+ /* Make sure the event is an appropriate type */
+ if (IsBtnEvent(event)) {
+ switch (screen->send_mouse_pos) {
+ case X10_MOUSE: /* X10 compatibility sequences */
+
+ if (BtnModifiers(my_event) == 0) {
+ if (my_event->type == ButtonPress)
+ EditorButton(xw, my_event);
+ result = True;
+ }
+ break;
+
+ case VT200_HIGHLIGHT_MOUSE: /* DEC vt200 hilite tracking */
+ if (my_event->type == ButtonPress &&
+ BtnModifiers(my_event) == 0 &&
+ my_event->button == Button1) {
+ TrackDown(xw, my_event);
+ result = True;
+ } else if (BtnModifiers(my_event) == 0
+ || BtnModifiers(my_event) == ControlMask) {
+ EditorButton(xw, my_event);
+ result = True;
+ }
+ break;
+
+ case VT200_MOUSE: /* DEC vt200 compatible */
+ if (BtnModifiers(my_event) == 0
+ || BtnModifiers(my_event) == ControlMask) {
+ EditorButton(xw, my_event);
+ result = True;
+ }
+ break;
+
+#if OPT_DEC_LOCATOR
+ case DEC_LOCATOR:
+ result = SendLocatorPosition(xw, my_event);
+ break;
+#endif /* OPT_DEC_LOCATOR */
+ }
+ }
+ }
+ return result;
+}
+
+#if OPT_DEC_LOCATOR
+
+#define LocatorCoords( row, col, x, y, oor ) \
+ if( screen->locator_pixels ) { \
+ (oor)=False; (row) = (y)+1; (col) = (x)+1; \
+ /* Limit to screen dimensions */ \
+ if ((row) < 1) (row) = 1,(oor)=True; \
+ else if ((row) > screen->border*2+Height(screen)) \
+ (row) = screen->border*2+Height(screen),(oor)=True; \
+ if ((col) < 1) (col) = 1,(oor)=True; \
+ else if ((col) > OriginX(screen)*2+Width(screen)) \
+ (col) = OriginX(screen)*2+Width(screen),(oor)=True; \
+ } else { \
+ (oor)=False; \
+ /* Compute character position of mouse pointer */ \
+ (row) = ((y) - screen->border) / FontHeight(screen); \
+ (col) = ((x) - OriginX(screen)) / FontWidth(screen); \
+ /* Limit to screen dimensions */ \
+ if ((row) < 0) (row) = 0,(oor)=True; \
+ else if ((row) > screen->max_row) \
+ (row) = screen->max_row,(oor)=True; \
+ if ((col) < 0) (col) = 0,(oor)=True; \
+ else if ((col) > screen->max_col) \
+ (col) = screen->max_col,(oor)=True; \
+ (row)++; (col)++; \
+ }
+
+static Bool
+SendLocatorPosition(XtermWidget xw, XButtonEvent * event)
+{
+ ANSI reply;
+ TScreen *screen = TScreenOf(xw);
+ int row, col;
+ Bool oor;
+ int button;
+ unsigned state;
+
+ /* Make sure the event is an appropriate type */
+ if ((!IsBtnEvent(event) &&
+ !screen->loc_filter) ||
+ (BtnModifiers(event) != 0 && BtnModifiers(event) != ControlMask))
+ return (False);
+
+ if ((event->type == ButtonPress &&
+ !(screen->locator_events & LOC_BTNS_DN)) ||
+ (event->type == ButtonRelease &&
+ !(screen->locator_events & LOC_BTNS_UP)))
+ return (True);
+
+ if (event->type == MotionNotify) {
+ CheckLocatorPosition(xw, event);
+ return (True);
+ }
+
+ /* get button # */
+ button = (int) event->button - 1;
+
+ LocatorCoords(row, col, event->x, event->y, oor);
+
+ /*
+ * DECterm mouse:
+ *
+ * ESCAPE '[' event ; mask ; row ; column '&' 'w'
+ */
+ memset(&reply, 0, sizeof(reply));
+ reply.a_type = ANSI_CSI;
+
+ if (oor) {
+ reply.a_nparam = 1;
+ reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+ return (True);
+ }
+
+ /*
+ * event:
+ * 1 no buttons
+ * 2 left button down
+ * 3 left button up
+ * 4 middle button down
+ * 5 middle button up
+ * 6 right button down
+ * 7 right button up
+ * 8 M4 down
+ * 9 M4 up
+ */
+ reply.a_nparam = 4;
+ switch (event->type) {
+ case ButtonPress:
+ reply.a_param[0] = (ParmType) (2 + (button << 1));
+ break;
+ case ButtonRelease:
+ reply.a_param[0] = (ParmType) (3 + (button << 1));
+ break;
+ default:
+ return (True);
+ }
+ /*
+ * mask:
+ * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ * M4 down left down middle down right down
+ *
+ * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
+ * Also, mask should be the state after the button press/release,
+ * X provides the state not including the button press/release.
+ */
+ state = (event->state
+ & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
+ /* update mask to "after" state */
+ state ^= ((unsigned) (1 << button));
+ /* swap Button1 & Button3 */
+ state = ((state & (unsigned) ~(4 | 1))
+ | ((state & 1) ? 4 : 0)
+ | ((state & 4) ? 1 : 0));
+
+ reply.a_param[1] = (ParmType) state;
+ reply.a_param[2] = (ParmType) row;
+ reply.a_param[3] = (ParmType) col;
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+
+ /*
+ * DECterm turns the Locator off if a button is pressed while a filter rectangle
+ * is active. This might be a bug, but I don't know, so I'll emulate it anyways.
+ */
+ if (screen->loc_filter) {
+ screen->send_mouse_pos = MOUSE_OFF;
+ screen->loc_filter = False;
+ screen->locator_events = 0;
+ MotionOff(screen, xw);
+ }
+
+ return (True);
+}
+
+/*
+ * mask:
+ * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ * M4 down left down middle down right down
+ *
+ * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
+ */
+#define ButtonState(state, mask) \
+{ (state) = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8); \
+ /* swap Button1 & Button3 */ \
+ (state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0); \
+}
+
+void
+GetLocatorPosition(XtermWidget xw)
+{
+ ANSI reply;
+ TScreen *screen = TScreenOf(xw);
+ Window root, child;
+ int rx, ry, x, y;
+ unsigned int mask;
+ int row = 0, col = 0;
+ Bool oor = False;
+ Bool ret = False;
+ int state;
+
+ /*
+ * DECterm turns the Locator off if the position is requested while a filter rectangle
+ * is active. This might be a bug, but I don't know, so I'll emulate it anyways.
+ */
+ if (screen->loc_filter) {
+ screen->send_mouse_pos = MOUSE_OFF;
+ screen->loc_filter = False;
+ screen->locator_events = 0;
+ MotionOff(screen, xw);
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ reply.a_type = ANSI_CSI;
+
+ if (screen->send_mouse_pos == DEC_LOCATOR) {
+ ret = XQueryPointer(screen->display, VWindow(screen), &root,
+ &child, &rx, &ry, &x, &y, &mask);
+ if (ret) {
+ LocatorCoords(row, col, x, y, oor);
+ }
+ }
+ if (ret == False || oor) {
+ reply.a_nparam = 1;
+ reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+ return;
+ }
+
+ ButtonState(state, mask);
+
+ reply.a_nparam = 4;
+ reply.a_param[0] = 1; /* Event - 1 = response to locator request */
+ reply.a_param[1] = (ParmType) state;
+ reply.a_param[2] = (ParmType) row;
+ reply.a_param[3] = (ParmType) col;
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+}
+
+void
+InitLocatorFilter(XtermWidget xw)
+{
+ ANSI reply;
+ TScreen *screen = TScreenOf(xw);
+ Window root, child;
+ int rx, ry, x, y;
+ unsigned int mask;
+ int row = 0, col = 0;
+ Bool oor = 0;
+ Bool ret;
+ int state;
+
+ ret = XQueryPointer(screen->display, VWindow(screen),
+ &root, &child, &rx, &ry, &x, &y, &mask);
+ if (ret) {
+ LocatorCoords(row, col, x, y, oor);
+ }
+ if (ret == False || oor) {
+ /* Locator is unavailable */
+
+ if (screen->loc_filter_top != LOC_FILTER_POS ||
+ screen->loc_filter_left != LOC_FILTER_POS ||
+ screen->loc_filter_bottom != LOC_FILTER_POS ||
+ screen->loc_filter_right != LOC_FILTER_POS) {
+ /*
+ * If any explicit coordinates were received,
+ * report immediately with no coordinates.
+ */
+ memset(&reply, 0, sizeof(reply));
+ reply.a_type = ANSI_CSI;
+ reply.a_nparam = 1;
+ reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+ } else {
+ /*
+ * No explicit coordinates were received, and the pointer is
+ * unavailable. Report when the pointer re-enters the window.
+ */
+ screen->loc_filter = True;
+ MotionOn(screen, xw);
+ }
+ return;
+ }
+
+ /*
+ * Adjust rectangle coordinates:
+ * 1. Replace "LOC_FILTER_POS" with current coordinates
+ * 2. Limit coordinates to screen size
+ * 3. make sure top and left are less than bottom and right, resp.
+ */
+ if (screen->locator_pixels) {
+ rx = OriginX(screen) * 2 + Width(screen);
+ ry = screen->border * 2 + Height(screen);
+ } else {
+ rx = screen->max_col;
+ ry = screen->max_row;
+ }
+
+#define Adjust( coord, def, max ) \
+ if( (coord) == LOC_FILTER_POS ) (coord) = (def); \
+ else if ((coord) < 1) (coord) = 1; \
+ else if ((coord) > (max)) (coord) = (max)
+
+ Adjust(screen->loc_filter_top, row, ry);
+ Adjust(screen->loc_filter_left, col, rx);
+ Adjust(screen->loc_filter_bottom, row, ry);
+ Adjust(screen->loc_filter_right, col, rx);
+
+ if (screen->loc_filter_top > screen->loc_filter_bottom) {
+ ry = screen->loc_filter_top;
+ screen->loc_filter_top = screen->loc_filter_bottom;
+ screen->loc_filter_bottom = ry;
+ }
+
+ if (screen->loc_filter_left > screen->loc_filter_right) {
+ rx = screen->loc_filter_left;
+ screen->loc_filter_left = screen->loc_filter_right;
+ screen->loc_filter_right = rx;
+ }
+
+ if ((col < screen->loc_filter_left) ||
+ (col > screen->loc_filter_right) ||
+ (row < screen->loc_filter_top) ||
+ (row > screen->loc_filter_bottom)) {
+ /* Pointer is already outside the rectangle - report immediately */
+ ButtonState(state, mask);
+
+ memset(&reply, 0, sizeof(reply));
+ reply.a_type = ANSI_CSI;
+ reply.a_nparam = 4;
+ reply.a_param[0] = 10; /* Event - 10 = locator outside filter */
+ reply.a_param[1] = (ParmType) state;
+ reply.a_param[2] = (ParmType) row;
+ reply.a_param[3] = (ParmType) col;
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+ return;
+ }
+
+ /*
+ * Rectangle is set up. Allow pointer tracking
+ * to detect if the mouse leaves the rectangle.
+ */
+ screen->loc_filter = True;
+ MotionOn(screen, xw);
+}
+
+static void
+CheckLocatorPosition(XtermWidget xw, XButtonEvent * event)
+{
+ ANSI reply;
+ TScreen *screen = TScreenOf(xw);
+ int row, col;
+ Bool oor;
+ int state;
+
+ LocatorCoords(row, col, event->x, event->y, oor);
+
+ /*
+ * Send report if the pointer left the filter rectangle, if
+ * the pointer left the window, or if the filter rectangle
+ * had no coordinates and the pointer re-entered the window.
+ */
+ if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
+ (col < screen->loc_filter_left) ||
+ (col > screen->loc_filter_right) ||
+ (row < screen->loc_filter_top) ||
+ (row > screen->loc_filter_bottom)) {
+ /* Filter triggered - disable it */
+ screen->loc_filter = False;
+ MotionOff(screen, xw);
+
+ memset(&reply, 0, sizeof(reply));
+ reply.a_type = ANSI_CSI;
+ if (oor) {
+ reply.a_nparam = 1;
+ reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
+ } else {
+ ButtonState(state, event->state);
+
+ reply.a_nparam = 4;
+ reply.a_param[0] = 10; /* Event - 10 = locator outside filter */
+ reply.a_param[1] = (ParmType) state;
+ reply.a_param[2] = (ParmType) row;
+ reply.a_param[3] = (ParmType) col;
+ }
+
+ reply.a_inters = '&';
+ reply.a_final = 'w';
+ unparseseq(xw, &reply);
+
+ if (screen->locator_reset) {
+ MotionOff(screen, xw);
+ screen->send_mouse_pos = MOUSE_OFF;
+ }
+ }
+}
+#endif /* OPT_DEC_LOCATOR */
+
+#if OPT_READLINE
+static int
+isClick1_clean(TScreen * screen, XButtonEvent * event)
+{
+ int delta;
+
+ if (!IsBtnEvent(event)
+ /* Disable on Shift-Click-1, including the application-mouse modes */
+ || (BtnModifiers(event) & ShiftMask)
+ || (screen->send_mouse_pos != MOUSE_OFF) /* Kinda duplicate... */
+ ||ExtendingSelection) /* Was moved */
+ return 0;
+
+ if (event->type != ButtonRelease)
+ return 0;
+
+ if (lastButtonDownTime == (Time) 0) {
+ /* first time or once in a blue moon */
+ delta = screen->multiClickTime + 1;
+ } else if (event->time > lastButtonDownTime) {
+ /* most of the time */
+ delta = (int) (event->time - lastButtonDownTime);
+ } else {
+ /* time has rolled over since lastButtonUpTime */
+ delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
+ }
+
+ return delta <= screen->multiClickTime;
+}
+
+static int
+isDoubleClick3(TScreen * screen, XButtonEvent * event)
+{
+ int delta;
+
+ if (event->type != ButtonRelease
+ || (BtnModifiers(event) & ShiftMask)
+ || event->button != Button3) {
+ lastButton3UpTime = 0; /* Disable the cached info */
+ return 0;
+ }
+ /* Process Btn3Release. */
+ if (lastButton3DoubleDownTime == (Time) 0) {
+ /* No previous click or once in a blue moon */
+ delta = screen->multiClickTime + 1;
+ } else if (event->time > lastButton3DoubleDownTime) {
+ /* most of the time */
+ delta = (int) (event->time - lastButton3DoubleDownTime);
+ } else {
+ /* time has rolled over since lastButton3DoubleDownTime */
+ delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
+ }
+ if (delta <= screen->multiClickTime) {
+ /* Double click */
+ CELL cell;
+
+ /* Cannot check ExtendingSelection, since mouse-3 always sets it */
+ PointToCELL(screen, event->y, event->x, &cell);
+ if (isSameCELL(&cell, &lastButton3)) {
+ lastButton3DoubleDownTime = 0; /* Disable the third click */
+ return 1;
+ }
+ }
+ /* Not a double click, memorize for future check. */
+ lastButton3UpTime = event->time;
+ PointToCELL(screen, event->y, event->x, &lastButton3);
+ return 0;
+}
+
+static int
+CheckSecondPress3(TScreen * screen, XEvent * event)
+{
+ int delta;
+
+ if (event->type != ButtonPress
+ || (KeyModifiers(event) & ShiftMask)
+ || event->xbutton.button != Button3) {
+ lastButton3DoubleDownTime = 0; /* Disable the cached info */
+ return 0;
+ }
+ /* Process Btn3Press. */
+ if (lastButton3UpTime == (Time) 0) {
+ /* No previous click or once in a blue moon */
+ delta = screen->multiClickTime + 1;
+ } else if (event->xbutton.time > lastButton3UpTime) {
+ /* most of the time */
+ delta = (int) (event->xbutton.time - lastButton3UpTime);
+ } else {
+ /* time has rolled over since lastButton3UpTime */
+ delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
+ }
+ if (delta <= screen->multiClickTime) {
+ CELL cell;
+
+ PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
+ if (isSameCELL(&cell, &lastButton3)) {
+ /* A candidate for a double-click */
+ lastButton3DoubleDownTime = event->xbutton.time;
+ PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
+ return 1;
+ }
+ lastButton3UpTime = 0; /* Disable the info about the previous click */
+ }
+ /* Either too long, or moved, disable. */
+ lastButton3DoubleDownTime = 0;
+ return 0;
+}
+
+static int
+rowOnCurrentLine(TScreen * screen,
+ int line,
+ int *deltap) /* must be XButtonEvent */
+{
+ int result = 1;
+ int l1, l2;
+
+ *deltap = 0;
+ if (line != screen->cur_row) {
+ if (line < screen->cur_row)
+ l1 = line, l2 = screen->cur_row;
+ else
+ l2 = line, l1 = screen->cur_row;
+ l1--;
+ while (++l1 < l2) {
+ LineData *ld = GET_LINEDATA(screen, l1);
+ if (!LineTstWrapped(ld)) {
+ result = 0;
+ break;
+ }
+ }
+ if (result) {
+ /* Everything is on one "wrapped line" now */
+ *deltap = line - screen->cur_row;
+ }
+ }
+ return result;
+}
+
+static int
+eventRow(TScreen * screen, XEvent * event) /* must be XButtonEvent */
+{
+ return (event->xbutton.y - screen->border) / FontHeight(screen);
+}
+
+static int
+eventColBetween(TScreen * screen, XEvent * event) /* must be XButtonEvent */
+{
+ /* Correct by half a width - we are acting on a boundary, not on a cell. */
+ return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
+ / FontWidth(screen));
+}
+
+static int
+ReadLineMovePoint(TScreen * screen, int col, int ldelta)
+{
+ Char line[6];
+ unsigned count = 0;
+
+ col += ldelta * MaxCols(screen) - screen->cur_col;
+ if (col == 0)
+ return 0;
+ if (screen->control_eight_bits) {
+ line[count++] = ANSI_CSI;
+ } else {
+ line[count++] = ANSI_ESC;
+ line[count++] = '['; /* XXX maybe sometimes O is better? */
+ }
+ line[count] = CharOf(col > 0 ? 'C' : 'D');
+ if (col < 0)
+ col = -col;
+ while (col--)
+ v_write(screen->respond, line, 3);
+ return 1;
+}
+
+static int
+ReadLineDelete(TScreen * screen, CELL * cell1, CELL * cell2)
+{
+ int del;
+
+ del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
+ if (del <= 0) /* Just in case... */
+ return 0;
+ while (del--)
+ v_write(screen->respond, (const Char *) "\177", 1);
+ return 1;
+}
+
+static void
+readlineExtend(TScreen * screen, XEvent * event)
+{
+ int ldelta1, ldelta2;
+
+ if (IsBtnEvent(event)) {
+ XButtonEvent *my_event = (XButtonEvent *) event;
+ if (isClick1_clean(screen, my_event)
+ && SCREEN_FLAG(screen, click1_moves)
+ && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
+ ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
+ }
+ if (isDoubleClick3(screen, my_event)
+ && SCREEN_FLAG(screen, dclick3_deletes)
+ && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
+ && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
+ ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
+ ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
+ }
+ }
+}
+
+#endif /* OPT_READLINE */
+
+/* ^XM-G<line+' '><col+' '> */
+void
+DiredButton(Widget w,
+ XEvent * event, /* must be XButtonEvent */
+ String * params GCC_UNUSED, /* selections */
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ Char Line[6];
+ unsigned line, col;
+
+ if (IsBtnEvent(event)
+ && (event->xbutton.y >= screen->border)
+ && (event->xbutton.x >= OriginX(screen))) {
+ line = (unsigned) ((event->xbutton.y - screen->border)
+ / FontHeight(screen));
+ col = (unsigned) ((event->xbutton.x - OriginX(screen))
+ / FontWidth(screen));
+ Line[0] = CONTROL('X');
+ Line[1] = ANSI_ESC;
+ Line[2] = 'G';
+ Line[3] = CharOf(' ' + col);
+ Line[4] = CharOf(' ' + line);
+ v_write(screen->respond, Line, 5);
+ }
+ }
+}
+
+#if OPT_READLINE
+void
+ReadLineButton(Widget w,
+ XEvent * event, /* must be XButtonEvent */
+ String * params GCC_UNUSED, /* selections */
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ Char Line[6];
+ int line, col, ldelta = 0;
+
+ if (!IsBtnEvent(event)
+ || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
+ goto finish;
+ if (event->type == ButtonRelease) {
+ int delta;
+
+ if (lastButtonDownTime == (Time) 0) {
+ /* first time and once in a blue moon */
+ delta = screen->multiClickTime + 1;
+ } else if (event->xbutton.time > lastButtonDownTime) {
+ /* most of the time */
+ delta = (int) (event->xbutton.time - lastButtonDownTime);
+ } else {
+ /* time has rolled over since lastButtonUpTime */
+ delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
+ }
+ if (delta > screen->multiClickTime)
+ goto finish; /* All this work for this... */
+ }
+ line = (event->xbutton.y - screen->border) / FontHeight(screen);
+ if (!rowOnCurrentLine(screen, line, &ldelta))
+ goto finish;
+ /* Correct by half a width - we are acting on a boundary, not on a cell. */
+ col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
+ / 2)
+ / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
+ if (col == 0)
+ goto finish;
+ Line[0] = ANSI_ESC;
+ /* XXX: sometimes it is better to send '['? */
+ Line[1] = 'O';
+ Line[2] = CharOf(col > 0 ? 'C' : 'D');
+ if (col < 0)
+ col = -col;
+ while (col--)
+ v_write(screen->respond, Line, 3);
+ finish:
+ if (event->type == ButtonRelease)
+ do_select_end(xw, event, params, num_params, False);
+ }
+}
+#endif /* OPT_READLINE */
+
+/* repeats <ESC>n or <ESC>p */
+void
+ViButton(Widget w,
+ XEvent * event, /* must be XButtonEvent */
+ String * params GCC_UNUSED, /* selections */
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ int pty = screen->respond;
+ Char Line[6];
+ int line;
+
+ if (IsBtnEvent(event)) {
+
+ line = screen->cur_row -
+ ((event->xbutton.y - screen->border) / FontHeight(screen));
+ if (line != 0) {
+ Line[0] = ANSI_ESC; /* force an exit from insert-mode */
+ v_write(pty, Line, 1);
+
+ if (line < 0) {
+ line = -line;
+ Line[0] = CONTROL('n');
+ } else {
+ Line[0] = CONTROL('p');
+ }
+ while (--line >= 0)
+ v_write(pty, Line, 1);
+ }
+ }
+ }
+}
+
+/*
+ * This function handles button-motion events
+ */
+/*ARGSUSED*/
+void
+HandleSelectExtend(Widget w,
+ XEvent * event, /* must be XMotionEvent */
+ String * params GCC_UNUSED,
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ CELL cell;
+
+ TRACE(("HandleSelectExtend\n"));
+
+ screen->selection_time = event->xmotion.time;
+ switch (screen->eventMode) {
+ /* If not in one of the DEC mouse-reporting modes */
+ case LEFTEXTENSION:
+ case RIGHTEXTENSION:
+ PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
+ ExtendExtend(xw, &cell);
+ break;
+
+ /* If in motion reporting mode, send mouse position to
+ character process as a key sequence \E[M... */
+ case NORMAL:
+ /* will get here if send_mouse_pos != MOUSE_OFF */
+ if (screen->send_mouse_pos == BTN_EVENT_MOUSE
+ || screen->send_mouse_pos == ANY_EVENT_MOUSE) {
+ (void) SendMousePosition(xw, event);
+ }
+ break;
+ }
+ }
+}
+
+void
+HandleKeyboardSelectExtend(Widget w,
+ XEvent * event GCC_UNUSED, /* must be XButtonEvent */
+ String * params GCC_UNUSED,
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("HandleKeyboardSelectExtend\n"));
+ ExtendExtend(xw, &screen->cursorp);
+ }
+}
+
+static void
+do_select_end(XtermWidget xw,
+ XEvent * event, /* must be XButtonEvent */
+ String * params, /* selections */
+ Cardinal *num_params,
+ Bool use_cursor_loc)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ screen->selection_time = event->xbutton.time;
+ switch (screen->eventMode) {
+ case NORMAL:
+ (void) SendMousePosition(xw, event);
+ break;
+ case LEFTEXTENSION:
+ case RIGHTEXTENSION:
+ EndExtend(xw, event, params, *num_params, use_cursor_loc);
+#if OPT_READLINE
+ readlineExtend(screen, event);
+#endif /* OPT_READLINE */
+ break;
+ }
+}
+
+void
+HandleSelectEnd(Widget w,
+ XEvent * event, /* must be XButtonEvent */
+ String * params, /* selections */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleSelectEnd\n"));
+ do_select_end(xw, event, params, num_params, False);
+ }
+}
+
+void
+HandleKeyboardSelectEnd(Widget w,
+ XEvent * event, /* must be XButtonEvent */
+ String * params, /* selections */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleKeyboardSelectEnd\n"));
+ do_select_end(xw, event, params, num_params, True);
+ }
+}
+
+/*
+ * Copy the selection data to the given target(s).
+ */
+void
+HandleCopySelection(Widget w,
+ XEvent * event,
+ String * params, /* list of targets */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleCopySelection\n"));
+ SelectSet(xw, event, params, *num_params);
+ }
+}
+
+struct _SelectionList {
+ String *params;
+ Cardinal count;
+ Atom *targets;
+ Time time;
+};
+
+static unsigned
+DECtoASCII(unsigned ch)
+{
+ if (xtermIsDecGraphic(ch)) {
+ ch = CharOf("###########+++++##-##++++|######"[ch]);
+ /* 01234567890123456789012345678901 */
+ }
+ return ch;
+}
+
+#if OPT_WIDE_CHARS
+static Cardinal
+addXtermChar(Char ** buffer, Cardinal *used, Cardinal offset, unsigned value)
+{
+ if (offset + 1 >= *used) {
+ *used = 1 + (2 * (offset + 1));
+ allocXtermChars(buffer, *used);
+ }
+ (*buffer)[offset++] = (Char) value;
+ return offset;
+}
+#define AddChar(buffer, used, offset, value) \
+ offset = addXtermChar(buffer, used, offset, (unsigned) value)
+
+/*
+ * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
+ * or ASCII/Latin-1 equivalents for special cases.
+ */
+static Char *
+UTF8toLatin1(TScreen * screen, Char * s, unsigned long len, unsigned long *result)
+{
+ static Char *buffer;
+ static Cardinal used;
+
+ Cardinal offset = 0;
+
+ const Char *p;
+
+ if (len != 0) {
+ PtyData data;
+
+ fakePtyData(&data, s, s + len);
+ while (decodeUtf8(&data)) {
+ Bool fails = False;
+ Bool extra = False;
+ IChar value = skipPtyData(&data);
+ if (value == UCS_REPL) {
+ fails = True;
+ } else if (value < 256) {
+ AddChar(&buffer, &used, offset, CharOf(value));
+ } else {
+ unsigned eqv = ucs2dec(value);
+ if (xtermIsDecGraphic(eqv)) {
+ AddChar(&buffer, &used, offset, DECtoASCII(eqv));
+ } else {
+ eqv = AsciiEquivs(value);
+ if (eqv == value) {
+ fails = True;
+ } else {
+ AddChar(&buffer, &used, offset, eqv);
+ }
+ if (isWide((wchar_t) value))
+ extra = True;
+ }
+ }
+
+ /*
+ * If we're not able to plug in a single-byte result, insert the
+ * defaultString (which normally is a single "#", but could be
+ * whatever the user wants).
+ */
+ if (fails) {
+ for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
+ AddChar(&buffer, &used, offset, *p);
+ }
+ }
+ if (extra)
+ AddChar(&buffer, &used, offset, ' ');
+ }
+ AddChar(&buffer, &used, offset, '\0');
+ *result = (unsigned long) (offset - 1);
+ } else {
+ *result = 0;
+ }
+ return buffer;
+}
+
+int
+xtermUtf8ToTextList(XtermWidget xw,
+ XTextProperty * text_prop,
+ char ***text_list,
+ int *text_list_count)
+{
+ TScreen *screen = TScreenOf(xw);
+ Display *dpy = screen->display;
+ int rc = -1;
+
+ if (text_prop->format == 8
+ && (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
+ text_list,
+ text_list_count)) >= 0) {
+ if (*text_list != NULL && *text_list_count != 0) {
+ int i;
+ Char *data;
+ char **new_text_list, *tmp;
+ unsigned long size, new_size;
+
+ TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
+
+ /*
+ * XLib StringList actually uses only two pointers, one for the
+ * list itself, and one for the data. Pointer to the data is the
+ * first element of the list, the rest (if any) list elements point
+ * to the same memory block as the first element
+ */
+ new_size = 0;
+ for (i = 0; i < *text_list_count; ++i) {
+ data = (Char *) (*text_list)[i];
+ size = strlen((*text_list)[i]) + 1;
+ (void) UTF8toLatin1(screen, data, size, &size);
+ new_size += size + 1;
+ }
+ new_text_list = TypeXtMallocN(char *, *text_list_count);
+ new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
+ for (i = 0; i < (*text_list_count); ++i) {
+ data = (Char *) (*text_list)[i];
+ size = strlen((*text_list)[i]) + 1;
+ data = UTF8toLatin1(screen, data, size, &size);
+ memcpy(tmp, data, size + 1);
+ new_text_list[i] = tmp;
+ tmp += size + 1;
+ }
+ XFreeStringList((*text_list));
+ *text_list = new_text_list;
+ } else {
+ rc = -1;
+ }
+ }
+ return rc;
+}
+#endif /* OPT_WIDE_CHARS */
+
+static char *
+parseItem(char *value, char *nextc)
+{
+ char *nextp = value;
+ while (*nextp != '\0' && *nextp != ',') {
+ *nextp = x_toupper(*nextp);
+ ++nextp;
+ }
+ *nextc = *nextp;
+ *nextp = '\0';
+ x_strtrim(value);
+
+ return nextp;
+}
+
+/*
+ * All of the wanted strings are unique in the first character, so we can
+ * use simple abbreviations.
+ */
+static Bool
+sameItem(const char *actual, const char *wanted)
+{
+ Bool result = False;
+ size_t have = strlen(actual);
+ size_t need = strlen(wanted);
+
+ if (have != 0 && have <= need) {
+ if (!strncmp(actual, wanted, have)) {
+ TRACE(("...matched \"%s\"\n", wanted));
+ result = True;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
+ */
+static Bool
+overrideTargets(Widget w, String value, Atom ** resultp)
+{
+ Bool override = False;
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+
+ if (!IsEmpty(value)) {
+ char *copied = x_strdup(value);
+ if (copied != 0) {
+ Atom *result = 0;
+ Cardinal count = 1;
+ int n;
+
+ TRACE(("decoding SelectTypes \"%s\"\n", value));
+ for (n = 0; copied[n] != '\0'; ++n) {
+ if (copied[n] == ',')
+ ++count;
+ }
+ result = TypeXtMallocN(Atom, (2 * count) + 1);
+ if (result == NULL) {
+ TRACE(("Couldn't allocate selection types\n"));
+ } else {
+ char nextc = '?';
+ char *listp = (char *) copied;
+ count = 0;
+ do {
+ char *nextp = parseItem(listp, &nextc);
+ size_t len = strlen(listp);
+
+ if (len == 0) {
+ /* EMPTY */ ;
+ }
+#if OPT_WIDE_CHARS
+ else if (sameItem(listp, "UTF8")) {
+ result[count++] = XA_UTF8_STRING(XtDisplay(w));
+ }
+#endif
+ else if (sameItem(listp, "I18N")) {
+ if (screen->i18nSelections) {
+ result[count++] = XA_TEXT(XtDisplay(w));
+ result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
+ }
+ } else if (sameItem(listp, "TEXT")) {
+ result[count++] = XA_TEXT(XtDisplay(w));
+ } else if (sameItem(listp, "COMPOUND_TEXT")) {
+ result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
+ } else if (sameItem(listp, "STRING")) {
+ result[count++] = XA_STRING;
+ }
+ *nextp++ = nextc;
+ listp = nextp;
+ } while (nextc != '\0');
+ if (count) {
+ result[count] = None;
+ override = True;
+ *resultp = result;
+ } else {
+ XtFree((char *) result);
+ }
+ }
+ } else {
+ TRACE(("Couldn't allocate copy of selection types\n"));
+ }
+ }
+ }
+ return override;
+}
+
+#if OPT_WIDE_CHARS
+static Atom *
+allocUtf8Targets(Widget w, TScreen * screen)
+{
+ Atom **resultp = &(screen->selection_targets_utf8);
+
+ if (*resultp == 0) {
+ Atom *result;
+
+ if (!overrideTargets(w, screen->utf8_select_types, &result)) {
+ result = TypeXtMallocN(Atom, 5);
+ if (result == NULL) {
+ TRACE(("Couldn't allocate utf-8 selection targets\n"));
+ } else {
+ int n = 0;
+
+ if (XSupportsLocale()) {
+ result[n++] = XA_UTF8_STRING(XtDisplay(w));
+#ifdef X_HAVE_UTF8_STRING
+ if (screen->i18nSelections) {
+ result[n++] = XA_TEXT(XtDisplay(w));
+ result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
+ }
+#endif
+ }
+ result[n++] = XA_STRING;
+ result[n] = None;
+ }
+ }
+
+ *resultp = result;
+ }
+
+ return *resultp;
+}
+#endif
+
+static Atom *
+alloc8bitTargets(Widget w, TScreen * screen)
+{
+ Atom **resultp = &(screen->selection_targets_8bit);
+
+ if (*resultp == 0) {
+ Atom *result = 0;
+
+ if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
+ result = TypeXtMallocN(Atom, 5);
+ if (result == NULL) {
+ TRACE(("Couldn't allocate 8bit selection targets\n"));
+ } else {
+ int n = 0;
+
+ if (XSupportsLocale()) {
+#ifdef X_HAVE_UTF8_STRING
+ result[n++] = XA_UTF8_STRING(XtDisplay(w));
+#endif
+ if (screen->i18nSelections) {
+ result[n++] = XA_TEXT(XtDisplay(w));
+ result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
+ }
+ }
+ result[n++] = XA_STRING;
+ result[n] = None;
+ }
+ }
+
+ *resultp = result;
+ }
+
+ return *resultp;
+}
+
+static Atom *
+_SelectionTargets(Widget w)
+{
+ Atom *result;
+ TScreen *screen;
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) == 0) {
+ result = NULL;
+ } else {
+ screen = TScreenOf(xw);
+
+#if OPT_WIDE_CHARS
+ if (screen->wide_chars) {
+ result = allocUtf8Targets(w, screen);
+ } else
+#endif
+ {
+ /* not screen->wide_chars */
+ result = alloc8bitTargets(w, screen);
+ }
+ }
+
+ return result;
+}
+
+#define isSELECT(value) (!strcmp(value, "SELECT"))
+
+static void
+UnmapSelections(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+ Cardinal n;
+
+ if (screen->mappedSelect) {
+ for (n = 0; screen->mappedSelect[n] != 0; ++n)
+ free((void *) screen->mappedSelect[n]);
+ free(screen->mappedSelect);
+ screen->mappedSelect = 0;
+ }
+}
+
+/*
+ * xterm generally uses the primary selection. Some applications prefer
+ * (or are limited to) the clipboard. Since the translations resource is
+ * complicated, users seldom change the way it affects selection. But it
+ * is simple to remap the choice between primary and clipboard before the
+ * call to XmuInternStrings().
+ */
+static String *
+MapSelections(XtermWidget xw, String * params, Cardinal num_params)
+{
+ String *result = params;
+
+ if (num_params > 0) {
+ Cardinal j;
+ Boolean map = False;
+
+ for (j = 0; j < num_params; ++j) {
+ TRACE(("param[%d]:%s\n", j, params[j]));
+ if (isSELECT(params[j])) {
+ map = True;
+ break;
+ }
+ }
+ if (map) {
+ TScreen *screen = TScreenOf(xw);
+ const char *mapTo = (screen->selectToClipboard
+ ? "CLIPBOARD"
+ : "PRIMARY");
+
+ UnmapSelections(xw);
+ if ((result = TypeMallocN(String, num_params + 1)) != 0) {
+ result[num_params] = 0;
+ for (j = 0; j < num_params; ++j) {
+ result[j] = x_strdup((isSELECT(params[j])
+ ? mapTo
+ : params[j]));
+ if (result[j] == 0) {
+ UnmapSelections(xw);
+ result = 0;
+ break;
+ }
+ }
+ screen->mappedSelect = result;
+ }
+ }
+ }
+ return result;
+}
+
+/*
+ * Lookup the cut-buffer number, which will be in the range 0-7.
+ * If it is not a cut-buffer, it is the primary selection (-1).
+ */
+static int
+CutBuffer(Atom code)
+{
+ int cutbuffer;
+ switch ((unsigned) code) {
+ case XA_CUT_BUFFER0:
+ cutbuffer = 0;
+ break;
+ case XA_CUT_BUFFER1:
+ cutbuffer = 1;
+ break;
+ case XA_CUT_BUFFER2:
+ cutbuffer = 2;
+ break;
+ case XA_CUT_BUFFER3:
+ cutbuffer = 3;
+ break;
+ case XA_CUT_BUFFER4:
+ cutbuffer = 4;
+ break;
+ case XA_CUT_BUFFER5:
+ cutbuffer = 5;
+ break;
+ case XA_CUT_BUFFER6:
+ cutbuffer = 6;
+ break;
+ case XA_CUT_BUFFER7:
+ cutbuffer = 7;
+ break;
+ default:
+ cutbuffer = -1;
+ break;
+ }
+ return cutbuffer;
+}
+
+#if OPT_PASTE64
+static void
+FinishPaste64(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
+ if (screen->base64_paste) {
+ screen->base64_paste = 0;
+ unparseputc1(xw, screen->base64_final);
+ unparse_end(xw);
+ }
+}
+#endif
+
+#if !OPT_PASTE64
+static
+#endif
+void
+xtermGetSelection(Widget w,
+ Time ev_time,
+ String * params, /* selections in precedence order */
+ Cardinal num_params,
+ Atom * targets)
+{
+ Atom selection;
+ int cutbuffer;
+ Atom target;
+
+ XtermWidget xw;
+
+ if (num_params == 0)
+ return;
+ if ((xw = getXtermWidget(w)) == 0)
+ return;
+
+ TRACE(("xtermGetSelection num_params %d\n", num_params));
+ params = MapSelections(xw, params, num_params);
+
+ XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
+ cutbuffer = CutBuffer(selection);
+
+ TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
+ (targets
+ ? visibleSelectionTarget(XtDisplay(w), targets[0])
+ : "None")));
+
+ if (cutbuffer >= 0) {
+ int inbytes;
+ unsigned long nbytes;
+ int fmt8 = 8;
+ Atom type = XA_STRING;
+ char *line;
+
+ /* 'line' is freed in SelectionReceived */
+ line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
+ nbytes = (unsigned long) inbytes;
+
+ if (nbytes > 0)
+ SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
+ &nbytes, &fmt8);
+ else if (num_params > 1) {
+ xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
+ }
+#if OPT_PASTE64
+ else {
+ FinishPaste64(xw);
+ }
+#endif
+ return;
+ } else {
+ struct _SelectionList *list;
+
+ if (targets == NULL || targets[0] == None) {
+ targets = _SelectionTargets(w);
+ }
+
+ if (targets != 0) {
+ target = targets[0];
+
+ if (targets[1] == None) { /* last target in list */
+ params++;
+ num_params--;
+ targets = _SelectionTargets(w);
+ } else {
+ targets = &(targets[1]);
+ }
+
+ if (num_params) {
+ /* 'list' is freed in SelectionReceived */
+ list = TypeXtMalloc(struct _SelectionList);
+ if (list != 0) {
+ list->params = params;
+ list->count = num_params;
+ list->targets = targets;
+ list->time = ev_time;
+ }
+ } else {
+ list = NULL;
+ }
+
+ XtGetSelectionValue(w, selection,
+ target,
+ SelectionReceived,
+ (XtPointer) list, ev_time);
+ }
+ }
+}
+
+#if OPT_TRACE && OPT_WIDE_CHARS
+static void
+GettingSelection(Display * dpy, Atom type, Char * line, unsigned long len)
+{
+ Char *cp;
+ char *name;
+
+ name = XGetAtomName(dpy, type);
+
+ TRACE(("Getting %s (%ld)\n", name, (long int) type));
+ for (cp = line; cp < line + len; cp++) {
+ TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
+ if (isprint(*cp)) {
+ TRACE(("%c\n", *cp));
+ } else {
+ TRACE(("\\x%02x\n", *cp));
+ }
+ }
+}
+#else
+#define GettingSelection(dpy,type,line,len) /* nothing */
+#endif
+
+#ifdef VMS
+# define tty_vwrite(pty,lag,l) tt_write(lag,l)
+#else /* !( VMS ) */
+# define tty_vwrite(pty,lag,l) v_write(pty,lag,l)
+#endif /* defined VMS */
+
+#if OPT_PASTE64
+/* Return base64 code character given 6-bit number */
+static const char base64_code[] = "\
+ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+abcdefghijklmnopqrstuvwxyz\
+0123456789+/";
+static void
+base64_flush(TScreen * screen)
+{
+ Char x;
+ switch (screen->base64_count) {
+ case 0:
+ break;
+ case 2:
+ x = CharOf(base64_code[screen->base64_accu << 4]);
+ tty_vwrite(screen->respond, &x, 1);
+ break;
+ case 4:
+ x = CharOf(base64_code[screen->base64_accu << 2]);
+ tty_vwrite(screen->respond, &x, 1);
+ break;
+ }
+ if (screen->base64_pad & 3)
+ tty_vwrite(screen->respond,
+ (const Char *) "===",
+ (unsigned) (4 - (screen->base64_pad & 3)));
+ screen->base64_count = 0;
+ screen->base64_accu = 0;
+ screen->base64_pad = 0;
+}
+#endif /* OPT_PASTE64 */
+
+static void
+_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length)
+{
+#if OPT_PASTE64
+ if (screen->base64_paste) {
+ /* Send data as base64 */
+ Char *p = lag;
+ Char buf[64];
+ unsigned x = 0;
+ while (length--) {
+ switch (screen->base64_count) {
+ case 0:
+ buf[x++] = CharOf(base64_code[*p >> 2]);
+ screen->base64_accu = (unsigned) (*p & 0x3);
+ screen->base64_count = 2;
+ ++p;
+ break;
+ case 2:
+ buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
+ (*p >> 4)]);
+ screen->base64_accu = (unsigned) (*p & 0xF);
+ screen->base64_count = 4;
+ ++p;
+ break;
+ case 4:
+ buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
+ (*p >> 6)]);
+ buf[x++] = CharOf(base64_code[*p & 0x3F]);
+ screen->base64_accu = 0;
+ screen->base64_count = 0;
+ ++p;
+ break;
+ }
+ if (x >= 63) {
+ /* Write 63 or 64 characters */
+ screen->base64_pad += x;
+ tty_vwrite(screen->respond, buf, x);
+ x = 0;
+ }
+ }
+ if (x != 0) {
+ screen->base64_pad += x;
+ tty_vwrite(screen->respond, buf, x);
+ }
+ } else
+#endif /* OPT_PASTE64 */
+#if OPT_READLINE
+ if (SCREEN_FLAG(screen, paste_quotes)) {
+ while (length--) {
+ tty_vwrite(screen->respond, (const Char *) "\026", 1); /* Control-V */
+ tty_vwrite(screen->respond, lag++, 1);
+ }
+ } else
+#endif
+ tty_vwrite(screen->respond, lag, length);
+}
+
+static void
+_WriteSelectionData(TScreen * screen, Char * line, size_t length)
+{
+ /* Write data to pty a line at a time. */
+ /* Doing this one line at a time may no longer be necessary
+ because v_write has been re-written. */
+
+ Char *lag, *end;
+
+ /* in the VMS version, if tt_pasting isn't set to True then qio
+ reads aren't blocked and an infinite loop is entered, where the
+ pasted text shows up as new input, goes in again, shows up
+ again, ad nauseum. */
+#ifdef VMS
+ tt_pasting = True;
+#endif
+
+ end = &line[length];
+ lag = line;
+
+#if OPT_PASTE64
+ if (screen->base64_paste) {
+ _qWriteSelectionData(screen, lag, (unsigned) (end - lag));
+ base64_flush(screen);
+ } else
+#endif
+ {
+ if (!SCREEN_FLAG(screen, paste_literal_nl)) {
+ Char *cp;
+ for (cp = line; cp != end; cp++) {
+ if (*cp == '\n') {
+ *cp = '\r';
+ _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1));
+ lag = cp + 1;
+ }
+ }
+ }
+
+ if (lag != end) {
+ _qWriteSelectionData(screen, lag, (unsigned) (end - lag));
+ }
+ }
+#ifdef VMS
+ tt_pasting = False;
+ tt_start_read(); /* reenable reads or a character may be lost */
+#endif
+}
+
+#if OPT_READLINE
+static void
+_WriteKey(TScreen * screen, const Char * in)
+{
+ Char line[16];
+ unsigned count = 0;
+ size_t length = strlen((const char *) in);
+
+ if (screen->control_eight_bits) {
+ line[count++] = ANSI_CSI;
+ } else {
+ line[count++] = ANSI_ESC;
+ line[count++] = '[';
+ }
+ while (length--)
+ line[count++] = *in++;
+ line[count++] = '~';
+ tty_vwrite(screen->respond, line, count);
+}
+#endif /* OPT_READLINE */
+
+/* SelectionReceived: stuff received selection text into pty */
+
+/* ARGSUSED */
+static void
+SelectionReceived(Widget w,
+ XtPointer client_data,
+ Atom * selection GCC_UNUSED,
+ Atom * type,
+ XtPointer value,
+ unsigned long *length,
+ int *format)
+{
+ char **text_list = NULL;
+ int text_list_count;
+ XTextProperty text_prop;
+ TScreen *screen;
+ Display *dpy;
+#if OPT_TRACE && OPT_WIDE_CHARS
+ Char *line = (Char *) value;
+#endif
+
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) == 0)
+ return;
+
+ screen = TScreenOf(xw);
+ dpy = XtDisplay(w);
+
+ if (*type == 0 /*XT_CONVERT_FAIL */
+ || *length == 0
+ || value == NULL)
+ goto fail;
+
+ text_prop.value = (unsigned char *) value;
+ text_prop.encoding = *type;
+ text_prop.format = *format;
+ text_prop.nitems = *length;
+
+ TRACE(("SelectionReceived %s format %d, nitems %ld\n",
+ visibleSelectionTarget(dpy, text_prop.encoding),
+ text_prop.format,
+ text_prop.nitems));
+
+#if OPT_WIDE_CHARS
+ if (XSupportsLocale() && screen->wide_chars) {
+ if (*type == XA_UTF8_STRING(dpy) ||
+ *type == XA_STRING ||
+ *type == XA_COMPOUND_TEXT(dpy)) {
+ GettingSelection(dpy, *type, line, *length);
+ if (Xutf8TextPropertyToTextList(dpy, &text_prop,
+ &text_list,
+ &text_list_count) < 0) {
+ TRACE(("default Xutf8 Conversion failed\n"));
+ text_list = NULL;
+ }
+ }
+ } else
+#endif /* OPT_WIDE_CHARS */
+ {
+ /* Convert the selection to locale's multibyte encoding. */
+
+ if (*type == XA_UTF8_STRING(dpy) ||
+ *type == XA_STRING ||
+ *type == XA_COMPOUND_TEXT(dpy)) {
+ Status rc;
+
+ GettingSelection(dpy, *type, line, *length);
+
+#if OPT_WIDE_CHARS
+ if (*type == XA_UTF8_STRING(dpy) &&
+ !(screen->wide_chars || screen->c1_printable)) {
+ rc = xtermUtf8ToTextList(xw, &text_prop,
+ &text_list, &text_list_count);
+ } else
+#endif
+ if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
+ rc = XTextPropertyToStringList(&text_prop,
+ &text_list, &text_list_count);
+ } else {
+ rc = XmbTextPropertyToTextList(dpy, &text_prop,
+ &text_list,
+ &text_list_count);
+ }
+ if (rc < 0) {
+ TRACE(("Conversion failed\n"));
+ text_list = NULL;
+ }
+ }
+ }
+
+ if (text_list != NULL && text_list_count != 0) {
+ int i;
+
+#if OPT_PASTE64
+ if (screen->base64_paste) {
+ /* EMPTY */ ;
+ } else
+#endif
+#if OPT_READLINE
+ if (SCREEN_FLAG(screen, paste_brackets)) {
+ _WriteKey(screen, (const Char *) "200");
+ }
+#endif
+ for (i = 0; i < text_list_count; i++) {
+ size_t len = strlen(text_list[i]);
+ if (screen->selectToBuffer) {
+ size_t have = (screen->internal_select
+ ? strlen(screen->internal_select)
+ : 0);
+ size_t need = have + len + 1;
+ char *buffer = realloc(screen->internal_select, need);
+ if (buffer != 0) {
+ strcpy(buffer + have, text_list[i]);
+ screen->internal_select = buffer;
+ }
+ } else {
+ _WriteSelectionData(screen, (Char *) text_list[i], len);
+ }
+ }
+#if OPT_PASTE64
+ if (screen->base64_paste) {
+ FinishPaste64(xw);
+ } else
+#endif
+#if OPT_READLINE
+ if (SCREEN_FLAG(screen, paste_brackets)) {
+ _WriteKey(screen, (const Char *) "201");
+ }
+#endif
+ XFreeStringList(text_list);
+ } else
+ goto fail;
+
+ XtFree((char *) client_data);
+ XtFree((char *) value);
+
+ return;
+
+ fail:
+ if (client_data != 0) {
+ struct _SelectionList *list = (struct _SelectionList *) client_data;
+
+ TRACE(("SelectionReceived ->xtermGetSelection\n"));
+ xtermGetSelection(w, list->time,
+ list->params, list->count, list->targets);
+ XtFree((char *) client_data);
+#if OPT_PASTE64
+ } else {
+ FinishPaste64(xw);
+#endif
+ }
+ return;
+}
+
+void
+HandleInsertSelection(Widget w,
+ XEvent * event, /* assumed to be XButtonEvent* */
+ String * params, /* selections in precedence order */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleInsertSelection\n"));
+ if (!SendMousePosition(xw, event)) {
+#if OPT_READLINE
+ int ldelta;
+ TScreen *screen = TScreenOf(xw);
+ if (IsBtnEvent(event)
+ /* Disable on Shift-mouse, including the application-mouse modes */
+ && !(KeyModifiers(event) & ShiftMask)
+ && (screen->send_mouse_pos == MOUSE_OFF)
+ && SCREEN_FLAG(screen, paste_moves)
+ && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
+ ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
+#endif /* OPT_READLINE */
+
+ xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
+ }
+ }
+}
+
+static SelectUnit
+EvalSelectUnit(XtermWidget xw,
+ Time buttonDownTime,
+ SelectUnit defaultUnit,
+ unsigned int button)
+{
+ TScreen *screen = TScreenOf(xw);
+ SelectUnit result;
+ int delta;
+
+ if (button != screen->lastButton) {
+ delta = screen->multiClickTime + 1;
+ } else if (screen->lastButtonUpTime == (Time) 0) {
+ /* first time and once in a blue moon */
+ delta = screen->multiClickTime + 1;
+ } else if (buttonDownTime > screen->lastButtonUpTime) {
+ /* most of the time */
+ delta = (int) (buttonDownTime - screen->lastButtonUpTime);
+ } else {
+ /* time has rolled over since lastButtonUpTime */
+ delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
+ }
+
+ if (delta > screen->multiClickTime) {
+ screen->numberOfClicks = 1;
+ result = defaultUnit;
+ } else {
+ result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
+ screen->numberOfClicks += 1;
+ }
+ TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
+ return result;
+}
+
+static void
+do_select_start(XtermWidget xw,
+ XEvent * event, /* must be XButtonEvent* */
+ CELL * cell)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (SendMousePosition(xw, event))
+ return;
+ screen->selectUnit = EvalSelectUnit(xw,
+ event->xbutton.time,
+ Select_CHAR,
+ event->xbutton.button);
+ screen->replyToEmacs = False;
+
+#if OPT_READLINE
+ lastButtonDownTime = event->xbutton.time;
+#endif
+
+ StartSelect(xw, cell);
+}
+
+/* ARGSUSED */
+void
+HandleSelectStart(Widget w,
+ XEvent * event, /* must be XButtonEvent* */
+ String * params GCC_UNUSED,
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ CELL cell;
+
+ TRACE(("HandleSelectStart\n"));
+ screen->firstValidRow = 0;
+ screen->lastValidRow = screen->max_row;
+ PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
+
+#if OPT_READLINE
+ ExtendingSelection = 0;
+#endif
+
+ do_select_start(xw, event, &cell);
+ }
+}
+
+/* ARGSUSED */
+void
+HandleKeyboardSelectStart(Widget w,
+ XEvent * event, /* must be XButtonEvent* */
+ String * params GCC_UNUSED,
+ Cardinal *num_params GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("HandleKeyboardSelectStart\n"));
+ do_select_start(xw, event, &screen->cursorp);
+ }
+}
+
+static void
+TrackDown(XtermWidget xw, XButtonEvent * event)
+{
+ TScreen *screen = TScreenOf(xw);
+ CELL cell;
+
+ screen->selectUnit = EvalSelectUnit(xw,
+ event->time,
+ Select_CHAR,
+ event->button);
+ if (screen->numberOfClicks > 1) {
+ PointToCELL(screen, event->y, event->x, &cell);
+ screen->replyToEmacs = True;
+ StartSelect(xw, &cell);
+ } else {
+ screen->waitingForTrackInfo = True;
+ EditorButton(xw, event);
+ }
+}
+
+#define boundsCheck(x) if (x < 0) \
+ x = 0; \
+ else if (x >= screen->max_row) \
+ x = screen->max_row
+
+void
+TrackMouse(XtermWidget xw,
+ int func,
+ CELL * start,
+ int firstrow,
+ int lastrow)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (screen->waitingForTrackInfo) { /* if Timed, ignore */
+ screen->waitingForTrackInfo = False;
+
+ if (func != 0) {
+ CELL first = *start;
+
+ boundsCheck(first.row);
+ boundsCheck(firstrow);
+ boundsCheck(lastrow);
+ screen->firstValidRow = firstrow;
+ screen->lastValidRow = lastrow;
+ screen->replyToEmacs = True;
+ StartSelect(xw, &first);
+ }
+ }
+}
+
+static void
+StartSelect(XtermWidget xw, const CELL * cell)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
+ if (screen->cursor_state)
+ HideCursor();
+ if (screen->numberOfClicks == 1) {
+ /* set start of selection */
+ screen->rawPos = *cell;
+ }
+ /* else use old values in rawPos */
+ screen->saveStartR = screen->startExt = screen->rawPos;
+ screen->saveEndR = screen->endExt = screen->rawPos;
+ if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
+ screen->eventMode = LEFTEXTENSION;
+ screen->startExt = *cell;
+ } else {
+ screen->eventMode = RIGHTEXTENSION;
+ screen->endExt = *cell;
+ }
+ ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
+}
+
+static void
+EndExtend(XtermWidget xw,
+ XEvent * event, /* must be XButtonEvent */
+ String * params, /* selections */
+ Cardinal num_params,
+ Bool use_cursor_loc)
+{
+ CELL cell;
+ unsigned count;
+ TScreen *screen = TScreenOf(xw);
+ Char line[64];
+
+ if (use_cursor_loc) {
+ cell = screen->cursorp;
+ } else {
+ PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
+ }
+ ExtendExtend(xw, &cell);
+ screen->lastButtonUpTime = event->xbutton.time;
+ screen->lastButton = event->xbutton.button;
+ if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
+ if (screen->replyToEmacs) {
+ count = 0;
+ if (screen->control_eight_bits) {
+ line[count++] = ANSI_CSI;
+ } else {
+ line[count++] = ANSI_ESC;
+ line[count++] = '[';
+ }
+ if (isSameCELL(&(screen->rawPos), &(screen->startSel))
+ && isSameCELL(&cell, &(screen->endSel))) {
+ /* Use short-form emacs select */
+
+ switch (screen->extend_coords) {
+ case 0:
+ case SET_EXT_MODE_MOUSE:
+ line[count++] = 't';
+ break;
+ case SET_SGR_EXT_MODE_MOUSE:
+ line[count++] = '<';
+ break;
+ }
+
+ count = EmitMousePosition(screen, line, count, screen->endSel.col);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, screen->endSel.row);
+
+ switch (screen->extend_coords) {
+ case SET_SGR_EXT_MODE_MOUSE:
+ case SET_URXVT_EXT_MODE_MOUSE:
+ line[count++] = 't';
+ break;
+ }
+ } else {
+ /* long-form, specify everything */
+
+ switch (screen->extend_coords) {
+ case 0:
+ case SET_EXT_MODE_MOUSE:
+ line[count++] = 'T';
+ break;
+ case SET_SGR_EXT_MODE_MOUSE:
+ line[count++] = '<';
+ break;
+ }
+
+ count = EmitMousePosition(screen, line, count, screen->startSel.col);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, screen->startSel.row);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, screen->endSel.col);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, screen->endSel.row);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, cell.col);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, cell.row);
+
+ switch (screen->extend_coords) {
+ case SET_SGR_EXT_MODE_MOUSE:
+ case SET_URXVT_EXT_MODE_MOUSE:
+ line[count++] = 'T';
+ break;
+ }
+ }
+ v_write(screen->respond, line, count);
+ TrackText(xw, &zeroCELL, &zeroCELL);
+ }
+ }
+ SelectSet(xw, event, params, num_params);
+ screen->eventMode = NORMAL;
+}
+
+void
+HandleSelectSet(Widget w,
+ XEvent * event,
+ String * params,
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleSelectSet\n"));
+ SelectSet(xw, event, params, *num_params);
+ }
+}
+
+/* ARGSUSED */
+static void
+SelectSet(XtermWidget xw,
+ XEvent * event GCC_UNUSED,
+ String * params,
+ Cardinal num_params)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("SelectSet\n"));
+ /* Only do select stuff if non-null select */
+ if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
+ SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
+ _OwnSelection(xw, params, num_params);
+ } else {
+ ScrnDisownSelection(xw);
+ }
+}
+
+#define Abs(x) ((x) < 0 ? -(x) : (x))
+
+/* ARGSUSED */
+static void
+do_start_extend(XtermWidget xw,
+ XEvent * event, /* must be XButtonEvent* */
+ String * params GCC_UNUSED,
+ Cardinal *num_params GCC_UNUSED,
+ Bool use_cursor_loc)
+{
+ TScreen *screen = TScreenOf(xw);
+ int coord;
+ CELL cell;
+
+ if (SendMousePosition(xw, event))
+ return;
+
+ screen->firstValidRow = 0;
+ screen->lastValidRow = screen->max_row;
+#if OPT_READLINE
+ if ((KeyModifiers(event) & ShiftMask)
+ || event->xbutton.button != Button3
+ || !(SCREEN_FLAG(screen, dclick3_deletes)))
+#endif
+ screen->selectUnit = EvalSelectUnit(xw,
+ event->xbutton.time,
+ screen->selectUnit,
+ event->xbutton.button);
+ screen->replyToEmacs = False;
+
+#if OPT_READLINE
+ CheckSecondPress3(screen, event);
+#endif
+
+ if (screen->numberOfClicks == 1
+ || (SCREEN_FLAG(screen, dclick3_deletes) /* Dclick special */
+ &&!(KeyModifiers(event) & ShiftMask))) {
+ /* Save existing selection so we can reestablish it if the guy
+ extends past the other end of the selection */
+ screen->saveStartR = screen->startExt = screen->startRaw;
+ screen->saveEndR = screen->endExt = screen->endRaw;
+ } else {
+ /* He just needed the selection mode changed, use old values. */
+ screen->startExt = screen->startRaw = screen->saveStartR;
+ screen->endExt = screen->endRaw = screen->saveEndR;
+ }
+ if (use_cursor_loc) {
+ cell = screen->cursorp;
+ } else {
+ PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
+ }
+ coord = Coordinate(screen, &cell);
+
+ if (Abs(coord - Coordinate(screen, &(screen->startSel)))
+ < Abs(coord - Coordinate(screen, &(screen->endSel)))
+ || coord < Coordinate(screen, &(screen->startSel))) {
+ /* point is close to left side of selection */
+ screen->eventMode = LEFTEXTENSION;
+ screen->startExt = cell;
+ } else {
+ /* point is close to left side of selection */
+ screen->eventMode = RIGHTEXTENSION;
+ screen->endExt = cell;
+ }
+ ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
+
+#if OPT_READLINE
+ if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
+ ExtendingSelection = 1;
+#endif
+}
+
+static void
+ExtendExtend(XtermWidget xw, const CELL * cell)
+{
+ TScreen *screen = TScreenOf(xw);
+ int coord = Coordinate(screen, cell);
+
+ TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
+ if (screen->eventMode == LEFTEXTENSION
+ && ((coord + (screen->selectUnit != Select_CHAR))
+ > Coordinate(screen, &(screen->endSel)))) {
+ /* Whoops, he's changed his mind. Do RIGHTEXTENSION */
+ screen->eventMode = RIGHTEXTENSION;
+ screen->startExt = screen->saveStartR;
+ } else if (screen->eventMode == RIGHTEXTENSION
+ && coord < Coordinate(screen, &(screen->startSel))) {
+ /* Whoops, he's changed his mind. Do LEFTEXTENSION */
+ screen->eventMode = LEFTEXTENSION;
+ screen->endExt = screen->saveEndR;
+ }
+ if (screen->eventMode == LEFTEXTENSION) {
+ screen->startExt = *cell;
+ } else {
+ screen->endExt = *cell;
+ }
+ ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
+
+#if OPT_READLINE
+ if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
+ ExtendingSelection = 1;
+#endif
+}
+
+void
+HandleStartExtend(Widget w,
+ XEvent * event, /* must be XButtonEvent* */
+ String * params, /* unused */
+ Cardinal *num_params) /* unused */
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleStartExtend\n"));
+ do_start_extend(xw, event, params, num_params, False);
+ }
+}
+
+void
+HandleKeyboardStartExtend(Widget w,
+ XEvent * event, /* must be XButtonEvent* */
+ String * params, /* unused */
+ Cardinal *num_params) /* unused */
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleKeyboardStartExtend\n"));
+ do_start_extend(xw, event, params, num_params, True);
+ }
+}
+
+void
+ScrollSelection(TScreen * screen, int amount, Bool always)
+{
+ int minrow = INX2ROW(screen, -screen->savedlines);
+ int maxrow = INX2ROW(screen, screen->max_row);
+ int maxcol = screen->max_col;
+
+#define scroll_update_one(cell) \
+ (cell)->row += amount; \
+ if ((cell)->row < minrow) { \
+ (cell)->row = minrow; \
+ (cell)->col = 0; \
+ } \
+ if ((cell)->row > maxrow) { \
+ (cell)->row = maxrow; \
+ (cell)->col = maxcol; \
+ }
+
+ scroll_update_one(&(screen->startRaw));
+ scroll_update_one(&(screen->endRaw));
+ scroll_update_one(&(screen->startSel));
+ scroll_update_one(&(screen->endSel));
+
+ scroll_update_one(&(screen->rawPos));
+
+ /*
+ * If we are told to scroll the selection but it lies outside the scrolling
+ * margins, then that could cause the selection to move (bad). It is not
+ * simple to fix, because this function is called both for the scrollbar
+ * actions as well as application scrolling. The 'always' flag is set in
+ * the former case. The rest of the logic handles the latter.
+ */
+ if (ScrnHaveSelection(screen)) {
+ int adjust;
+
+ adjust = ROW2INX(screen, screen->startH.row);
+ if (always
+ || !ScrnHaveRowMargins(screen)
+ || ScrnIsRowInMargins(screen, adjust)) {
+ scroll_update_one(&screen->startH);
+ }
+ adjust = ROW2INX(screen, screen->endH.row);
+ if (always
+ || !ScrnHaveRowMargins(screen)
+ || ScrnIsRowInMargins(screen, adjust)) {
+ scroll_update_one(&screen->endH);
+ }
+ }
+
+ screen->startHCoord = Coordinate(screen, &screen->startH);
+ screen->endHCoord = Coordinate(screen, &screen->endH);
+}
+
+/*ARGSUSED*/
+void
+ResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
+{
+ rows--; /* decr to get 0-max */
+ cols--;
+
+ if (screen->startRaw.row > rows)
+ screen->startRaw.row = rows;
+ if (screen->startSel.row > rows)
+ screen->startSel.row = rows;
+ if (screen->endRaw.row > rows)
+ screen->endRaw.row = rows;
+ if (screen->endSel.row > rows)
+ screen->endSel.row = rows;
+ if (screen->rawPos.row > rows)
+ screen->rawPos.row = rows;
+
+ if (screen->startRaw.col > cols)
+ screen->startRaw.col = cols;
+ if (screen->startSel.col > cols)
+ screen->startSel.col = cols;
+ if (screen->endRaw.col > cols)
+ screen->endRaw.col = cols;
+ if (screen->endSel.col > cols)
+ screen->endSel.col = cols;
+ if (screen->rawPos.col > cols)
+ screen->rawPos.col = cols;
+}
+
+#if OPT_WIDE_CHARS
+Bool
+iswide(int i)
+{
+ return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
+}
+
+#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
+#endif
+
+static void
+PointToCELL(TScreen * screen,
+ int y,
+ int x,
+ CELL * cell)
+/* Convert pixel coordinates to character coordinates.
+ Rows are clipped between firstValidRow and lastValidRow.
+ Columns are clipped between to be 0 or greater, but are not clipped to some
+ maximum value. */
+{
+ cell->row = (y - screen->border) / FontHeight(screen);
+ if (cell->row < screen->firstValidRow)
+ cell->row = screen->firstValidRow;
+ else if (cell->row > screen->lastValidRow)
+ cell->row = screen->lastValidRow;
+ cell->col = (x - OriginX(screen)) / FontWidth(screen);
+ if (cell->col < 0)
+ cell->col = 0;
+ else if (cell->col > MaxCols(screen)) {
+ cell->col = MaxCols(screen);
+ }
+#if OPT_WIDE_CHARS
+ /*
+ * If we got a click on the right half of a doublewidth character,
+ * pretend it happened on the left half.
+ */
+ if (cell->col > 0
+ && isWideCell(cell->row, cell->col - 1)
+ && (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
+ cell->col -= 1;
+ }
+#endif
+}
+
+/*
+ * Find the last column at which text was drawn on the given row.
+ */
+static int
+LastTextCol(TScreen * screen, LineData * ld, int row)
+{
+ int i = -1;
+ Char *ch;
+
+ if (ld != 0) {
+ if (okScrnRow(screen, row)) {
+ for (i = screen->max_col,
+ ch = ld->attribs + i;
+ i >= 0 && !(*ch & CHARDRAWN);
+ ch--, i--) {
+ ;
+ }
+#if OPT_DEC_CHRSET
+ if (CSET_DOUBLE(GetLineDblCS(ld))) {
+ i *= 2;
+ }
+#endif
+ }
+ }
+ return (i);
+}
+
+#if !OPT_WIDE_CHARS
+/*
+** double click table for cut and paste in 8 bits
+**
+** This table is divided in four parts :
+**
+** - control characters [0,0x1f] U [0x80,0x9f]
+** - separators [0x20,0x3f] U [0xa0,0xb9]
+** - binding characters [0x40,0x7f] U [0xc0,0xff]
+** - exceptions
+*/
+/* *INDENT-OFF* */
+static int charClass[256] =
+{
+/* NUL SOH STX ETX EOT ENQ ACK BEL */
+ 32, 1, 1, 1, 1, 1, 1, 1,
+/* BS HT NL VT FF CR SO SI */
+ 1, 32, 1, 1, 1, 1, 1, 1,
+/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* CAN EM SUB ESC FS GS RS US */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* SP ! " # $ % & ' */
+ 32, 33, 34, 35, 36, 37, 38, 39,
+/* ( ) * + , - . / */
+ 40, 41, 42, 43, 44, 45, 46, 47,
+/* 0 1 2 3 4 5 6 7 */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* 8 9 : ; < = > ? */
+ 48, 48, 58, 59, 60, 61, 62, 63,
+/* @ A B C D E F G */
+ 64, 48, 48, 48, 48, 48, 48, 48,
+/* H I J K L M N O */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* P Q R S T U V W */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* X Y Z [ \ ] ^ _ */
+ 48, 48, 48, 91, 92, 93, 94, 48,
+/* ` a b c d e f g */
+ 96, 48, 48, 48, 48, 48, 48, 48,
+/* h i j k l m n o */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* p q r s t u v w */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* x y z { | } ~ DEL */
+ 48, 48, 48, 123, 124, 125, 126, 1,
+/* x80 x81 x82 x83 IND NEL SSA ESA */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* HTS HTJ VTS PLD PLU RI SS2 SS3 */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* DCS PU1 PU2 STS CCH MW SPA EPA */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* x98 x99 x9A CSI ST OSC PM APC */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* - i c/ L ox Y- | So */
+ 160, 161, 162, 163, 164, 165, 166, 167,
+/* .. c0 ip << _ R0 - */
+ 168, 169, 170, 171, 172, 173, 174, 175,
+/* o +- 2 3 ' u q| . */
+ 176, 177, 178, 179, 180, 181, 182, 183,
+/* , 1 2 >> 1/4 1/2 3/4 ? */
+ 184, 185, 186, 187, 188, 189, 190, 191,
+/* A` A' A^ A~ A: Ao AE C, */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* E` E' E^ E: I` I' I^ I: */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* D- N~ O` O' O^ O~ O: X */
+ 48, 48, 48, 48, 48, 48, 48, 215,
+/* O/ U` U' U^ U: Y' P B */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* a` a' a^ a~ a: ao ae c, */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* e` e' e^ e: i` i' i^ i: */
+ 48, 48, 48, 48, 48, 48, 48, 48,
+/* d n~ o` o' o^ o~ o: -: */
+ 48, 48, 48, 48, 48, 48, 48, 247,
+/* o/ u` u' u^ u: y' P y: */
+ 48, 48, 48, 48, 48, 48, 48, 48};
+/* *INDENT-ON* */
+
+int
+SetCharacterClassRange(int low, /* in range of [0..255] */
+ int high,
+ int value) /* arbitrary */
+{
+
+ if (low < 0 || high > 255 || high < low)
+ return (-1);
+
+ for (; low <= high; low++)
+ charClass[low] = value;
+
+ return (0);
+}
+#endif
+
+static int
+class_of(LineData * ld, CELL * cell)
+{
+ CELL temp = *cell;
+
+#if OPT_DEC_CHRSET
+ if (CSET_DOUBLE(GetLineDblCS(ld))) {
+ temp.col /= 2;
+ }
+#endif
+
+ assert(temp.col < (int) ld->lineSize);
+ return CharacterClass((int) (ld->charData[temp.col]));
+}
+
+#if OPT_WIDE_CHARS
+#define CClassSelects(name, cclass) \
+ (CClassOf(name) == cclass \
+ || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
+#else
+#define CClassSelects(name, cclass) \
+ (class_of(ld.name, &((screen->name))) == cclass)
+#endif
+
+#define CClassOf(name) class_of(ld.name, &((screen->name)))
+
+/*
+ * If the given column is past the end of text on the given row, bump to the
+ * beginning of the next line.
+ */
+static Boolean
+okPosition(TScreen * screen,
+ LineData ** ld,
+ CELL * cell)
+{
+ Boolean result = True;
+
+ if (cell->row > screen->max_row) {
+ result = False;
+ } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
+ if (cell->row < screen->max_row) {
+ cell->col = 0;
+ *ld = GET_LINEDATA(screen, ++cell->row);
+ result = False;
+ }
+ }
+ return result;
+}
+
+static void
+trimLastLine(TScreen * screen,
+ LineData ** ld,
+ CELL * last)
+{
+ if (screen->cutNewline && last->row < screen->max_row) {
+ last->col = 0;
+ *ld = GET_LINEDATA(screen, ++last->row);
+ } else {
+ last->col = LastTextCol(screen, *ld, last->row) + 1;
+ }
+}
+
+#if OPT_SELECT_REGEX
+/*
+ * Returns the first row of a wrapped line.
+ */
+static int
+firstRowOfLine(TScreen * screen, int row, Bool visible)
+{
+ LineData *ld = 0;
+ int limit = visible ? 0 : -screen->savedlines;
+
+ while (row > limit &&
+ (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
+ LineTstWrapped(ld)) {
+ --row;
+ }
+ return row;
+}
+
+/*
+ * Returns the last row of a wrapped line.
+ */
+static int
+lastRowOfLine(TScreen * screen, int row)
+{
+ LineData *ld;
+
+ while (row < screen->max_row &&
+ (ld = GET_LINEDATA(screen, row)) != 0 &&
+ LineTstWrapped(ld)) {
+ ++row;
+ }
+ return row;
+}
+
+/*
+ * Returns the number of cells on the range of rows.
+ */
+static unsigned
+lengthOfLines(TScreen * screen, int firstRow, int lastRow)
+{
+ unsigned length = 0;
+ int n;
+
+ for (n = firstRow; n <= lastRow; ++n) {
+ LineData *ld = GET_LINEDATA(screen, n);
+ int value = LastTextCol(screen, ld, n);
+ if (value >= 0)
+ length += (unsigned) (value + 1);
+ }
+ return length;
+}
+
+/*
+ * Make a copy of the wrapped-line which corresponds to the given row as a
+ * string of bytes. Construct an index for the columns from the beginning of
+ * the line.
+ */
+static char *
+make_indexed_text(TScreen * screen, int row, unsigned length, int *indexed)
+{
+ Char *result = 0;
+ size_t need = (length + 1);
+
+ /*
+ * Get a quick upper bound to the number of bytes needed, if the whole
+ * string were UTF-8.
+ */
+ if_OPT_WIDE_CHARS(screen, {
+ need *= ((screen->lineExtra + 1) * 6);
+ });
+
+ if ((result = TypeCallocN(Char, need + 1)) != 0) {
+ LineData *ld = GET_LINEDATA(screen, row);
+ unsigned used = 0;
+ Char *last = result;
+
+ do {
+ int col = 0;
+ int limit = LastTextCol(screen, ld, row);
+
+ while (col <= limit) {
+ Char *next = last;
+ unsigned data = ld->charData[col];
+
+ assert(col < (int) ld->lineSize);
+ /* some internal points may not be drawn */
+ if (data == 0)
+ data = ' ';
+
+ if_WIDE_OR_NARROW(screen, {
+ next = convertToUTF8(last, data);
+ }
+ , {
+ *next++ = CharOf(data);
+ });
+
+ if_OPT_WIDE_CHARS(screen, {
+ size_t off;
+ for_each_combData(off, ld) {
+ data = ld->combData[off][col];
+ if (data == 0)
+ break;
+ next = convertToUTF8(next, data);
+ }
+ });
+
+ indexed[used] = (int) (last - result);
+ *next = 0;
+ /* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
+ last = next;
+ ++used;
+ ++col;
+ indexed[used] = (int) (next - result);
+ }
+ } while (used < length &&
+ LineTstWrapped(ld) &&
+ (ld = GET_LINEDATA(screen, ++row)) != 0 &&
+ row < screen->max_row);
+ }
+ /* TRACE(("result:%s\n", result)); */
+ return (char *) result;
+}
+
+/*
+ * Find the column given an offset into the character string by using the
+ * index constructed in make_indexed_text().
+ */
+static int
+indexToCol(int *indexed, int len, int off)
+{
+ int col = 0;
+ while (indexed[col] < len) {
+ if (indexed[col] >= off)
+ break;
+ ++col;
+ }
+ return col;
+}
+
+/*
+ * Given a row number, and a column offset from that (which may be wrapped),
+ * set the cell to the actual row/column values.
+ */
+static void
+columnToCell(TScreen * screen, int row, int col, CELL * cell)
+{
+ while (row < screen->max_row) {
+ LineData *ld = GET_LINEDATA(screen, row);
+ int last = LastTextCol(screen, ld, row);
+
+ /* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
+ if (col <= last) {
+ break;
+ }
+ /*
+ * Stop if the current row does not wrap (does not continue the current
+ * line).
+ */
+ if (!LineTstWrapped(ld)) {
+ col = last + 1;
+ break;
+ }
+ col -= (last + 1);
+ ++row;
+ }
+ if (col < 0)
+ col = 0;
+ cell->row = row;
+ cell->col = col;
+}
+
+/*
+ * Given a cell, find the corresponding column offset.
+ */
+static int
+cellToColumn(TScreen * screen, CELL * cell)
+{
+ LineData *ld = 0;
+ int col = cell->col;
+ int row = firstRowOfLine(screen, cell->row, False);
+ while (row < cell->row) {
+ ld = GET_LINEDATA(screen, row);
+ col += LastTextCol(screen, ld, row++);
+ }
+#if OPT_DEC_CHRSET
+ if (ld == 0)
+ ld = GET_LINEDATA(screen, row);
+ if (CSET_DOUBLE(GetLineDblCS(ld)))
+ col /= 2;
+#endif
+ return col;
+}
+
+static void
+do_select_regex(TScreen * screen, CELL * startc, CELL * endc)
+{
+ LineData *ld = GET_LINEDATA(screen, startc->row);
+ int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
+ char *expr = screen->selectExpr[inx];
+ regex_t preg;
+ regmatch_t match;
+ char *search;
+ int *indexed;
+
+ TRACE(("Select_REGEX:%s\n", NonNull(expr)));
+ if (okPosition(screen, &ld, startc) && expr != 0) {
+ if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
+ int firstRow = firstRowOfLine(screen, startc->row, True);
+ int lastRow = lastRowOfLine(screen, firstRow);
+ unsigned size = lengthOfLines(screen, firstRow, lastRow);
+ int actual = cellToColumn(screen, startc);
+
+ TRACE(("regcomp ok rows %d..%d bytes %d\n",
+ firstRow, lastRow, size));
+
+ if ((indexed = TypeCallocN(int, size + 1)) != 0) {
+ if ((search = make_indexed_text(screen,
+ firstRow,
+ size,
+ indexed)) != 0) {
+ int len = (int) strlen(search);
+ int col;
+ int best_col = -1;
+ int best_len = -1;
+
+ for (col = 0; indexed[col] < len; ++col) {
+ if (regexec(&preg,
+ search + indexed[col],
+ (size_t) 1, &match, 0) == 0) {
+ int start_inx = match.rm_so + indexed[col];
+ int finis_inx = match.rm_eo + indexed[col];
+ int start_col = indexToCol(indexed, len, start_inx);
+ int finis_col = indexToCol(indexed, len, finis_inx);
+
+ if (start_col <= actual &&
+ actual < finis_col) {
+ int test = finis_col - start_col;
+ if (best_len < test) {
+ best_len = test;
+ best_col = start_col;
+ TRACE(("match column %d len %d\n",
+ best_col,
+ best_len));
+ }
+ }
+ }
+ }
+ if (best_col >= 0) {
+ int best_nxt = best_col + best_len;
+ columnToCell(screen, firstRow, best_col, startc);
+ columnToCell(screen, firstRow, best_nxt, endc);
+ TRACE(("search::%s\n", search));
+ TRACE(("indexed:%d..%d -> %d..%d\n",
+ best_col, best_nxt,
+ indexed[best_col],
+ indexed[best_nxt]));
+ TRACE(("matched:%d:%s\n",
+ indexed[best_nxt] + 1 -
+ indexed[best_col],
+ visibleChars((Char *) (search + indexed[best_col]),
+ (unsigned) (indexed[best_nxt] +
+ 1 -
+ indexed[best_col]))));
+ }
+ free(search);
+ }
+ free(indexed);
+#if OPT_DEC_CHRSET
+ if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
+ if (CSET_DOUBLE(GetLineDblCS(ld)))
+ startc->col *= 2;
+ }
+ if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
+ if (CSET_DOUBLE(GetLineDblCS(ld)))
+ endc->col *= 2;
+ }
+#endif
+ }
+ regfree(&preg);
+ }
+ }
+}
+#endif /* OPT_SELECT_REGEX */
+
+#define InitRow(name) \
+ ld.name = GET_LINEDATA(screen, screen->name.row)
+
+#define NextRow(name) \
+ ld.name = GET_LINEDATA(screen, ++screen->name.row)
+
+#define PrevRow(name) \
+ ld.name = GET_LINEDATA(screen, --screen->name.row)
+
+#define MoreRows(name) \
+ (screen->name.row < screen->max_row)
+
+#define isPrevWrapped(name) \
+ (screen->name.row > 0 \
+ && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
+ && LineTstWrapped(ltmp))
+
+/*
+ * sets startSel endSel
+ * ensuring that they have legal values
+ */
+static void
+ComputeSelect(XtermWidget xw,
+ CELL * startc,
+ CELL * endc,
+ Bool extend)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ int length;
+ int cclass;
+ CELL first = *startc;
+ CELL last = *endc;
+ Boolean ignored = False;
+
+ struct {
+ LineData *startSel;
+ LineData *endSel;
+ } ld;
+ LineData *ltmp;
+
+ TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
+ first.row, first.col,
+ last.row, last.col,
+ extend ? "" : "no"));
+
+#if OPT_WIDE_CHARS
+ if (first.col > 1
+ && isWideCell(first.row, first.col - 1)
+ && XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
+ TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
+ first.col -= 1;
+ if (last.col == (first.col + 1))
+ last.col--;
+ }
+
+ if (last.col > 1
+ && isWideCell(last.row, last.col - 1)
+ && XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
+ last.col += 1;
+ }
+#endif
+
+ if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
+ screen->startSel = screen->startRaw = first;
+ screen->endSel = screen->endRaw = last;
+ } else { /* Swap them */
+ screen->startSel = screen->startRaw = last;
+ screen->endSel = screen->endRaw = first;
+ }
+
+ InitRow(startSel);
+ InitRow(endSel);
+
+ switch (screen->selectUnit) {
+ case Select_CHAR:
+ (void) okPosition(screen, &(ld.startSel), &(screen->startSel));
+ (void) okPosition(screen, &(ld.endSel), &(screen->endSel));
+ break;
+
+ case Select_WORD:
+ TRACE(("Select_WORD\n"));
+ if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
+ cclass = CClassOf(startSel);
+ do {
+ --screen->startSel.col;
+ if (screen->startSel.col < 0
+ && isPrevWrapped(startSel)) {
+ PrevRow(startSel);
+ screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
+ }
+ } while (screen->startSel.col >= 0
+ && CClassSelects(startSel, cclass));
+ ++screen->startSel.col;
+ }
+#if OPT_WIDE_CHARS
+ if (screen->startSel.col
+ && XTERM_CELL(screen->startSel.row,
+ screen->startSel.col) == HIDDEN_CHAR)
+ screen->startSel.col++;
+#endif
+
+ if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
+ length = LastTextCol(screen, ld.endSel, screen->endSel.row);
+ cclass = CClassOf(endSel);
+ do {
+ ++screen->endSel.col;
+ if (screen->endSel.col > length
+ && LineTstWrapped(ld.endSel)) {
+ if (!MoreRows(endSel))
+ break;
+ screen->endSel.col = 0;
+ NextRow(endSel);
+ length = LastTextCol(screen, ld.endSel, screen->endSel.row);
+ }
+ } while (screen->endSel.col <= length
+ && CClassSelects(endSel, cclass));
+ /* Word-select selects if pointing to any char in "word",
+ * especially note that it includes the last character in a word.
+ * So we do no --endSel.col and do special eol handling.
+ */
+ if (screen->endSel.col > length + 1
+ && MoreRows(endSel)) {
+ screen->endSel.col = 0;
+ NextRow(endSel);
+ }
+ }
+#if OPT_WIDE_CHARS
+ if (screen->endSel.col
+ && XTERM_CELL(screen->endSel.row,
+ screen->endSel.col) == HIDDEN_CHAR)
+ screen->endSel.col++;
+#endif
+
+ screen->saveStartW = screen->startSel;
+ break;
+
+ case Select_LINE:
+ TRACE(("Select_LINE\n"));
+ while (LineTstWrapped(ld.endSel)
+ && MoreRows(endSel)) {
+ NextRow(endSel);
+ }
+ if (screen->cutToBeginningOfLine
+ || screen->startSel.row < screen->saveStartW.row) {
+ screen->startSel.col = 0;
+ while (isPrevWrapped(startSel)) {
+ PrevRow(startSel);
+ }
+ } else if (!extend) {
+ if ((first.row < screen->saveStartW.row)
+ || (isSameRow(&first, &(screen->saveStartW))
+ && first.col < screen->saveStartW.col)) {
+ screen->startSel.col = 0;
+ while (isPrevWrapped(startSel)) {
+ PrevRow(startSel);
+ }
+ } else {
+ screen->startSel = screen->saveStartW;
+ }
+ }
+ trimLastLine(screen, &(ld.endSel), &(screen->endSel));
+ break;
+
+ case Select_GROUP: /* paragraph */
+ TRACE(("Select_GROUP\n"));
+ if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
+ /* scan backward for beginning of group */
+ while (screen->startSel.row > 0 &&
+ (LastTextCol(screen, ld.startSel, screen->startSel.row -
+ 1) > 0 ||
+ isPrevWrapped(startSel))) {
+ PrevRow(startSel);
+ }
+ screen->startSel.col = 0;
+ /* scan forward for end of group */
+ while (MoreRows(endSel) &&
+ (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
+ 0 ||
+ LineTstWrapped(ld.endSel))) {
+ NextRow(endSel);
+ }
+ trimLastLine(screen, &(ld.endSel), &(screen->endSel));
+ }
+ break;
+
+ case Select_PAGE: /* everything one can see */
+ TRACE(("Select_PAGE\n"));
+ screen->startSel.row = 0;
+ screen->startSel.col = 0;
+ screen->endSel.row = MaxRows(screen);
+ screen->endSel.col = 0;
+ break;
+
+ case Select_ALL: /* counts scrollback if in normal screen */
+ TRACE(("Select_ALL\n"));
+ screen->startSel.row = -screen->savedlines;
+ screen->startSel.col = 0;
+ screen->endSel.row = MaxRows(screen);
+ screen->endSel.col = 0;
+ break;
+
+#if OPT_SELECT_REGEX
+ case Select_REGEX:
+ do_select_regex(screen, &(screen->startSel), &(screen->endSel));
+ break;
+#endif
+
+ case NSELECTUNITS: /* always ignore */
+ ignored = True;
+ break;
+ }
+
+ if (!ignored) {
+ /* check boundaries */
+ ScrollSelection(screen, 0, False);
+ TrackText(xw, &(screen->startSel), &(screen->endSel));
+ }
+
+ return;
+}
+
+/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
+static void
+TrackText(XtermWidget xw,
+ const CELL * firstp,
+ const CELL * lastp)
+{
+ TScreen *screen = TScreenOf(xw);
+ int from, to;
+ CELL old_start, old_end;
+ CELL first = *firstp;
+ CELL last = *lastp;
+
+ TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
+ first.row, first.col, last.row, last.col));
+
+ old_start = screen->startH;
+ old_end = screen->endH;
+ TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
+ old_start.row, old_start.col,
+ old_end.row, old_end.col));
+ if (isSameCELL(&first, &old_start) &&
+ isSameCELL(&last, &old_end)) {
+ return;
+ }
+
+ screen->startH = first;
+ screen->endH = last;
+ from = Coordinate(screen, &screen->startH);
+ to = Coordinate(screen, &screen->endH);
+ if (to <= screen->startHCoord || from > screen->endHCoord) {
+ /* No overlap whatsoever between old and new hilite */
+ ReHiliteText(xw, &old_start, &old_end);
+ ReHiliteText(xw, &first, &last);
+ } else {
+ if (from < screen->startHCoord) {
+ /* Extend left end */
+ ReHiliteText(xw, &first, &old_start);
+ } else if (from > screen->startHCoord) {
+ /* Shorten left end */
+ ReHiliteText(xw, &old_start, &first);
+ }
+ if (to > screen->endHCoord) {
+ /* Extend right end */
+ ReHiliteText(xw, &old_end, &last);
+ } else if (to < screen->endHCoord) {
+ /* Shorten right end */
+ ReHiliteText(xw, &last, &old_end);
+ }
+ }
+ screen->startHCoord = from;
+ screen->endHCoord = to;
+}
+
+/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
+static void
+ReHiliteText(XtermWidget xw,
+ CELL * firstp,
+ CELL * lastp)
+{
+ TScreen *screen = TScreenOf(xw);
+ int i;
+ CELL first = *firstp;
+ CELL last = *lastp;
+
+ TRACE(("ReHiliteText from %d.%d to %d.%d\n",
+ first.row, first.col, last.row, last.col));
+
+ if (first.row < 0)
+ first.row = first.col = 0;
+ else if (first.row > screen->max_row)
+ return; /* nothing to do, since last.row >= first.row */
+
+ if (last.row < 0)
+ return; /* nothing to do, since first.row <= last.row */
+ else if (last.row > screen->max_row) {
+ last.row = screen->max_row;
+ last.col = MaxCols(screen);
+ }
+ if (isSameCELL(&first, &last))
+ return;
+
+ if (!isSameRow(&first, &last)) { /* do multiple rows */
+ if ((i = screen->max_col - first.col + 1) > 0) { /* first row */
+ ScrnRefresh(xw, first.row, first.col, 1, i, True);
+ }
+ if ((i = last.row - first.row - 1) > 0) { /* middle rows */
+ ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
+ }
+ if (last.col > 0 && last.row <= screen->max_row) { /* last row */
+ ScrnRefresh(xw, last.row, 0, 1, last.col, True);
+ }
+ } else { /* do single row */
+ ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
+ }
+}
+
+/*
+ * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
+ * (may have cell->row = screen->max_row+1, cell->col = 0).
+ */
+static void
+SaltTextAway(XtermWidget xw,
+ CELL * cellc,
+ CELL * cell)
+{
+ TScreen *screen = TScreenOf(xw);
+ int i, j = 0;
+ int eol;
+ int tmp;
+ Char *line;
+ Char *lp;
+ CELL first = *cellc;
+ CELL last = *cell;
+
+ if (isSameRow(&first, &last) && first.col > last.col) {
+ EXCHANGE(first.col, last.col, tmp);
+ }
+
+ --last.col;
+ /* first we need to know how long the string is before we can save it */
+
+ if (isSameRow(&last, &first)) {
+ j = Length(screen, first.row, first.col, last.col);
+ } else { /* two cases, cut is on same line, cut spans multiple lines */
+ j += Length(screen, first.row, first.col, screen->max_col) + 1;
+ for (i = first.row + 1; i < last.row; i++)
+ j += Length(screen, i, 0, screen->max_col) + 1;
+ if (last.col >= 0)
+ j += Length(screen, last.row, 0, last.col);
+ }
+
+ /* UTF-8 may require more space */
+ if_OPT_WIDE_CHARS(screen, {
+ j *= 4;
+ });
+
+ /* now get some memory to save it in */
+
+ if (screen->selection_size <= j) {
+ if ((line = (Char *) malloc((size_t) j + 1)) == 0)
+ SysError(ERROR_BMALLOC2);
+ XtFree((char *) screen->selection_data);
+ screen->selection_data = line;
+ screen->selection_size = j + 1;
+ } else {
+ line = screen->selection_data;
+ }
+
+ if ((line == 0)
+ || (j < 0))
+ return;
+
+ line[j] = '\0'; /* make sure it is null terminated */
+ lp = line; /* lp points to where to save the text */
+ if (isSameRow(&last, &first)) {
+ lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
+ } else {
+ lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
+ if (eol)
+ *lp++ = '\n'; /* put in newline at end of line */
+ for (i = first.row + 1; i < last.row; i++) {
+ lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
+ if (eol)
+ *lp++ = '\n';
+ }
+ if (last.col >= 0)
+ lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
+ }
+ *lp = '\0'; /* make sure we have end marked */
+
+ TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
+ visibleChars(line, (unsigned) (lp - line))));
+
+ screen->selection_length = (unsigned long) (lp - line);
+}
+
+#if OPT_PASTE64
+void
+ClearSelectionBuffer(TScreen * screen)
+{
+ screen->selection_length = 0;
+ screen->base64_count = 0;
+}
+
+static void
+AppendStrToSelectionBuffer(TScreen * screen, Char * text, size_t len)
+{
+ if (len != 0) {
+ int j = (int) (screen->selection_length + len); /* New length */
+ int k = j + (j >> 2) + 80; /* New size if we grow buffer: grow by ~50% */
+ if (j + 1 >= screen->selection_size) {
+ if (!screen->selection_length) {
+ /* New buffer */
+ Char *line;
+ if ((line = (Char *) malloc((size_t) k)) == 0)
+ SysError(ERROR_BMALLOC2);
+ XtFree((char *) screen->selection_data);
+ screen->selection_data = line;
+ } else {
+ /* Realloc buffer */
+ screen->selection_data = (Char *)
+ realloc(screen->selection_data,
+ (size_t) k);
+ if (screen->selection_data == 0)
+ SysError(ERROR_BMALLOC2);
+ }
+ screen->selection_size = k;
+ }
+ if (screen->selection_data != 0) {
+ memcpy(screen->selection_data + screen->selection_length, text, len);
+ screen->selection_length += len;
+ screen->selection_data[screen->selection_length] = 0;
+ }
+ }
+}
+
+void
+AppendToSelectionBuffer(TScreen * screen, unsigned c)
+{
+ unsigned six;
+ Char ch;
+
+ /* Decode base64 character */
+ if (c >= 'A' && c <= 'Z')
+ six = c - 'A';
+ else if (c >= 'a' && c <= 'z')
+ six = c - 'a' + 26;
+ else if (c >= '0' && c <= '9')
+ six = c - '0' + 52;
+ else if (c == '+')
+ six = 62;
+ else if (c == '/')
+ six = 63;
+ else
+ return;
+
+ /* Accumulate bytes */
+ switch (screen->base64_count) {
+ case 0:
+ screen->base64_accu = six;
+ screen->base64_count = 6;
+ break;
+
+ case 2:
+ ch = CharOf((screen->base64_accu << 6) + six);
+ screen->base64_count = 0;
+ AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
+ break;
+
+ case 4:
+ ch = CharOf((screen->base64_accu << 4) + (six >> 2));
+ screen->base64_accu = (six & 0x3);
+ screen->base64_count = 2;
+ AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
+ break;
+
+ case 6:
+ ch = CharOf((screen->base64_accu << 2) + (six >> 4));
+ screen->base64_accu = (six & 0xF);
+ screen->base64_count = 4;
+ AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
+ break;
+ }
+}
+
+void
+CompleteSelection(XtermWidget xw, String * args, Cardinal len)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ screen->base64_count = 0;
+ screen->base64_accu = 0;
+ _OwnSelection(xw, args, len);
+}
+#endif /* OPT_PASTE64 */
+
+static Bool
+_ConvertSelectionHelper(Widget w,
+ Atom * type,
+ XtPointer *value,
+ unsigned long *length,
+ int *format,
+ int (*conversion_function) (Display *,
+ char **, int,
+ XICCEncodingStyle,
+ XTextProperty *),
+ XICCEncodingStyle conversion_style)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ Display *dpy = XtDisplay(w);
+ XTextProperty textprop;
+ char *the_data = (char *) screen->selection_data;
+
+ if (conversion_function(dpy, &the_data, 1,
+ conversion_style,
+ &textprop) >= Success) {
+ *value = (XtPointer) textprop.value;
+ *length = textprop.nitems;
+ *type = textprop.encoding;
+ *format = textprop.format;
+ return True;
+ }
+ }
+ return False;
+}
+
+static Boolean
+SaveConvertedLength(XtPointer *target, unsigned long source)
+{
+ Boolean result = False;
+
+ *target = XtMalloc(4);
+ if (*target != 0) {
+ result = True;
+ if (sizeof(unsigned long) == 4) {
+ *(unsigned long *) *target = source;
+ } else if (sizeof(unsigned) == 4) {
+ *(unsigned *) *target = (unsigned) source;
+ } else if (sizeof(unsigned short) == 4) {
+ *(unsigned short *) *target = (unsigned short) source;
+ } else {
+ /* FIXME - does this depend on byte-order? */
+ unsigned long temp = source;
+ memcpy((char *) *target,
+ ((char *) &temp) + sizeof(temp) - 4,
+ (size_t) 4);
+ }
+ }
+ return result;
+}
+
+static Boolean
+ConvertSelection(Widget w,
+ Atom * selection,
+ Atom * target,
+ Atom * type,
+ XtPointer *value,
+ unsigned long *length,
+ int *format)
+{
+ Display *dpy = XtDisplay(w);
+ TScreen *screen;
+ Bool result = False;
+
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) == 0)
+ return False;
+
+ screen = TScreenOf(xw);
+
+ if (screen->selection_data == NULL)
+ return False; /* can this happen? */
+
+ TRACE(("ConvertSelection %s\n",
+ visibleSelectionTarget(dpy, *target)));
+
+ if (*target == XA_TARGETS(dpy)) {
+ Atom *allocP;
+ Atom *targetP;
+ Atom *std_targets;
+ XPointer std_return = 0;
+ unsigned long std_length;
+
+ if (XmuConvertStandardSelection(w, screen->selection_time, selection,
+ target, type, &std_return,
+ &std_length, format)) {
+ Atom *my_targets = _SelectionTargets(w);
+
+ TRACE(("XmuConvertStandardSelection - success\n"));
+ std_targets = (Atom *) (void *) (std_return);
+ *length = std_length + 6;
+
+ targetP = TypeXtMallocN(Atom, *length);
+ allocP = targetP;
+
+ *value = (XtPointer) targetP;
+
+ while (*my_targets != None) {
+ *targetP++ = *my_targets++;
+ }
+ *targetP++ = XA_LENGTH(dpy);
+ *targetP++ = XA_LIST_LENGTH(dpy);
+
+ *length = std_length + (unsigned long) (targetP - allocP);
+
+ memcpy(targetP, std_targets, sizeof(Atom) * std_length);
+ XtFree((char *) std_targets);
+ *type = XA_ATOM;
+ *format = 32;
+ result = True;
+ } else {
+ TRACE(("XmuConvertStandardSelection - failed\n"));
+ }
+ }
+#if OPT_WIDE_CHARS
+ else if (screen->wide_chars && *target == XA_STRING) {
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ Xutf8TextListToTextProperty,
+ XStringStyle);
+ TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
+ } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ Xutf8TextListToTextProperty,
+ XUTF8StringStyle);
+ TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
+ } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ Xutf8TextListToTextProperty,
+ XStdICCTextStyle);
+ TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
+ } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ Xutf8TextListToTextProperty,
+ XCompoundTextStyle);
+ TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
+ }
+#endif
+
+ else if (*target == XA_STRING) { /* not wide_chars */
+ /* We can only reach this point if the selection requestor
+ requested STRING before any of TEXT, COMPOUND_TEXT or
+ UTF8_STRING. We therefore assume that the requestor is not
+ properly internationalised, and dump raw eight-bit data
+ with no conversion into the selection. Yes, this breaks
+ the ICCCM in non-Latin-1 locales. */
+ *type = XA_STRING;
+ *value = (XtPointer) screen->selection_data;
+ *length = screen->selection_length;
+ *format = 8;
+ result = True;
+ TRACE(("...raw 8-bit data:%d\n", result));
+ } else if (*target == XA_TEXT(dpy)) { /* not wide_chars */
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ XmbTextListToTextProperty,
+ XStdICCTextStyle);
+ TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
+ } else if (*target == XA_COMPOUND_TEXT(dpy)) { /* not wide_chars */
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ XmbTextListToTextProperty,
+ XCompoundTextStyle);
+ TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
+ }
+#ifdef X_HAVE_UTF8_STRING
+ else if (*target == XA_UTF8_STRING(dpy)) { /* not wide_chars */
+ result =
+ _ConvertSelectionHelper(w,
+ type, value, length, format,
+ XmbTextListToTextProperty,
+ XUTF8StringStyle);
+ TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
+ }
+#endif
+ else if (*target == XA_LIST_LENGTH(dpy)) {
+ result = SaveConvertedLength(value, (unsigned long) 1);
+ *type = XA_INTEGER;
+ *length = 1;
+ *format = 32;
+ TRACE(("...list of values:%d\n", result));
+ } else if (*target == XA_LENGTH(dpy)) {
+ /* This value is wrong if we have UTF-8 text */
+ result = SaveConvertedLength(value, screen->selection_length);
+ *type = XA_INTEGER;
+ *length = 1;
+ *format = 32;
+ TRACE(("...list of values:%d\n", result));
+ } else if (XmuConvertStandardSelection(w,
+ screen->selection_time, selection,
+ target, type, (XPointer *) value,
+ length, format)) {
+ result = True;
+ TRACE(("...XmuConvertStandardSelection:%d\n", result));
+ }
+
+ /* else */
+ return (Boolean) result;
+}
+
+static void
+LoseSelection(Widget w, Atom * selection)
+{
+ TScreen *screen;
+ Atom *atomP;
+ Cardinal i;
+
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) == 0)
+ return;
+
+ screen = TScreenOf(xw);
+ for (i = 0, atomP = screen->selection_atoms;
+ i < screen->selection_count; i++, atomP++) {
+ if (*selection == *atomP)
+ *atomP = (Atom) 0;
+ if (CutBuffer(*atomP) >= 0) {
+ *atomP = (Atom) 0;
+ }
+ }
+
+ for (i = screen->selection_count; i; i--) {
+ if (screen->selection_atoms[i - 1] != 0)
+ break;
+ }
+ screen->selection_count = i;
+
+ for (i = 0, atomP = screen->selection_atoms;
+ i < screen->selection_count; i++, atomP++) {
+ if (*atomP == (Atom) 0) {
+ *atomP = screen->selection_atoms[--screen->selection_count];
+ }
+ }
+
+ if (screen->selection_count == 0)
+ TrackText(xw, &zeroCELL, &zeroCELL);
+}
+
+/* ARGSUSED */
+static void
+SelectionDone(Widget w GCC_UNUSED,
+ Atom * selection GCC_UNUSED,
+ Atom * target GCC_UNUSED)
+{
+ /* empty proc so Intrinsics know we want to keep storage */
+}
+
+static void
+_OwnSelection(XtermWidget xw,
+ String * selections,
+ Cardinal count)
+{
+ TScreen *screen = TScreenOf(xw);
+ Atom *atoms = screen->selection_atoms;
+ Cardinal i;
+ Bool have_selection = False;
+
+ if (count == 0)
+ return;
+ if (screen->selection_length == 0)
+ return;
+
+ TRACE(("_OwnSelection count %d\n", count));
+ selections = MapSelections(xw, selections, count);
+
+ if (count > screen->sel_atoms_size) {
+ XtFree((char *) atoms);
+ atoms = TypeXtMallocN(Atom, count);
+ screen->selection_atoms = atoms;
+ screen->sel_atoms_size = count;
+ }
+ XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
+ for (i = 0; i < count; i++) {
+ int cutbuffer = CutBuffer(atoms[i]);
+ if (cutbuffer >= 0) {
+ unsigned long limit =
+ (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
+ if (screen->selection_length > limit) {
+ TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
+ screen->selection_length, cutbuffer));
+ xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
+ screen->selection_length, cutbuffer);
+ } else {
+ /* This used to just use the UTF-8 data, which was totally
+ * broken as not even the corresponding paste code in Xterm
+ * understood this! So now it converts to Latin1 first.
+ * Robert Brady, 2000-09-05
+ */
+ unsigned long length = screen->selection_length;
+ Char *data = screen->selection_data;
+ if_OPT_WIDE_CHARS((screen), {
+ data = UTF8toLatin1(screen, data, length, &length);
+ });
+ TRACE(("XStoreBuffer(%d)\n", cutbuffer));
+ XStoreBuffer(XtDisplay((Widget) xw),
+ (char *) data,
+ (int) length,
+ cutbuffer);
+ }
+ } else if (!screen->replyToEmacs) {
+ have_selection |=
+ XtOwnSelection((Widget) xw, atoms[i],
+ screen->selection_time,
+ ConvertSelection, LoseSelection, SelectionDone);
+ }
+ }
+ if (!screen->replyToEmacs)
+ screen->selection_count = count;
+ if (!have_selection)
+ TrackText(xw, &zeroCELL, &zeroCELL);
+}
+
+static void
+ResetSelectionState(TScreen * screen)
+{
+ screen->selection_count = 0;
+ screen->startH = zeroCELL;
+ screen->endH = zeroCELL;
+}
+
+void
+DisownSelection(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+ Atom *atoms = screen->selection_atoms;
+ Cardinal count = screen->selection_count;
+ Cardinal i;
+
+ TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
+ count,
+ screen->startH.row,
+ screen->startH.col,
+ screen->endH.row,
+ screen->endH.col));
+
+ for (i = 0; i < count; i++) {
+ int cutbuffer = CutBuffer(atoms[i]);
+ if (cutbuffer < 0) {
+ XtDisownSelection((Widget) xw, atoms[i],
+ screen->selection_time);
+ }
+ }
+ /*
+ * If none of the callbacks via XtDisownSelection() reset highlighting
+ * do it now.
+ */
+ if (ScrnHaveSelection(screen)) {
+ /* save data which will be reset */
+ CELL first = screen->startH;
+ CELL last = screen->endH;
+
+ ResetSelectionState(screen);
+ ReHiliteText(xw, &first, &last);
+ } else {
+ ResetSelectionState(screen);
+ }
+}
+
+void
+UnhiliteSelection(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (ScrnHaveSelection(screen)) {
+ CELL first = screen->startH;
+ CELL last = screen->endH;
+
+ screen->startH = zeroCELL;
+ screen->endH = zeroCELL;
+ ReHiliteText(xw, &first, &last);
+ }
+}
+
+/* returns number of chars in line from scol to ecol out */
+/* ARGSUSED */
+static int
+Length(TScreen * screen,
+ int row,
+ int scol,
+ int ecol)
+{
+ LineData *ld = GET_LINEDATA(screen, row);
+ int lastcol = LastTextCol(screen, ld, row);
+
+ if (ecol > lastcol)
+ ecol = lastcol;
+ return (ecol - scol + 1);
+}
+
+/* copies text into line, preallocated */
+static Char *
+SaveText(TScreen * screen,
+ int row,
+ int scol,
+ int ecol,
+ Char * lp, /* pointer to where to put the text */
+ int *eol)
+{
+ LineData *ld;
+ int i = 0;
+ unsigned c;
+ Char *result = lp;
+#if OPT_WIDE_CHARS
+ unsigned previous = 0;
+#endif
+
+ ld = GET_LINEDATA(screen, row);
+ i = Length(screen, row, scol, ecol);
+ ecol = scol + i;
+#if OPT_DEC_CHRSET
+ if (CSET_DOUBLE(GetLineDblCS(ld))) {
+ scol = (scol + 0) / 2;
+ ecol = (ecol + 1) / 2;
+ }
+#endif
+ *eol = !LineTstWrapped(ld);
+ for (i = scol; i < ecol; i++) {
+ assert(i < (int) ld->lineSize);
+ c = E2A(ld->charData[i]);
+#if OPT_WIDE_CHARS
+ /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
+ * wide character.
+ */
+ if (c == HIDDEN_CHAR && isWide((int) previous)) {
+ previous = c;
+ /* Combining characters attached to double-width characters
+ are in memory attached to the HIDDEN_CHAR */
+ if_OPT_WIDE_CHARS(screen, {
+ if (screen->utf8_mode != uFalse) {
+ unsigned ch;
+ size_t off;
+ for_each_combData(off, ld) {
+ ch = ld->combData[off][i];
+ if (ch == 0)
+ break;
+ lp = convertToUTF8(lp, ch);
+ }
+ }
+ });
+ continue;
+ }
+ previous = c;
+ if (screen->utf8_mode != uFalse) {
+ lp = convertToUTF8(lp, (c != 0) ? c : ' ');
+ if_OPT_WIDE_CHARS(screen, {
+ unsigned ch;
+ size_t off;
+ for_each_combData(off, ld) {
+ ch = ld->combData[off][i];
+ if (ch == 0)
+ break;
+ lp = convertToUTF8(lp, ch);
+ }
+ });
+ } else
+#endif
+ {
+ if (c == 0) {
+ c = E2A(' ');
+ } else if (c < E2A(' ')) {
+ c = DECtoASCII(c);
+ } else if (c == 0x7f) {
+ c = 0x5f;
+ }
+ *lp++ = CharOf(A2E(c));
+ }
+ if (c != E2A(' '))
+ result = lp;
+ }
+
+ /*
+ * If requested, trim trailing blanks from selected lines. Do not do this
+ * if the line is wrapped.
+ */
+ if (!*eol || !screen->trim_selection)
+ result = lp;
+
+ return (result);
+}
+
+/* 32 + following 7-bit word:
+
+ 1:0 Button no: 0, 1, 2. 3=release.
+ 2 shift
+ 3 meta
+ 4 ctrl
+ 5 set for motion notify
+ 6 set for wheel
+*/
+
+/* Position: 32 - 255. */
+static int
+BtnCode(XButtonEvent * event, int button)
+{
+ int result = (int) (32 + (KeyState(event->state) << 2));
+
+ if (button < 0 || button > 5) {
+ result += 3;
+ } else {
+ if (button > 3)
+ result += (64 - 4);
+ if (event->type == MotionNotify)
+ result += 32;
+ result += button;
+ }
+ return result;
+}
+
+static unsigned
+EmitButtonCode(TScreen * screen,
+ Char * line,
+ unsigned count,
+ XButtonEvent * event,
+ int button)
+{
+ int value;
+
+ if (screen->send_mouse_pos == X10_MOUSE) {
+ value = CharOf(' ' + button);
+ } else {
+ value = BtnCode(event, button);
+ }
+
+ switch (screen->extend_coords) {
+ default:
+ line[count++] = CharOf(value);
+ break;
+ case SET_SGR_EXT_MODE_MOUSE:
+ value -= 32; /* encoding starts at zero */
+ /* FALLTHRU */
+ case SET_URXVT_EXT_MODE_MOUSE:
+ count += (unsigned) sprintf((char *) line + count, "%d", value);
+ break;
+ case SET_EXT_MODE_MOUSE:
+ if (value < 128) {
+ line[count++] = CharOf(value);
+ } else {
+ line[count++] = CharOf(0xC0 + (value >> 6));
+ line[count++] = CharOf(0x80 + (value & 0x3F));
+ }
+ break;
+ }
+ return count;
+}
+
+#define EMIT_BUTTON(button) EmitButtonCode(screen, line, count, event, button)
+
+static void
+EditorButton(XtermWidget xw, XButtonEvent * event)
+{
+ TScreen *screen = TScreenOf(xw);
+ int pty = screen->respond;
+ int mouse_limit = MouseLimit(screen);
+ Char line[32];
+ Char final = 'M';
+ int row, col;
+ int button;
+ unsigned count = 0;
+ Boolean changed = True;
+
+ /* If button event, get button # adjusted for DEC compatibility */
+ button = (int) (event->button - 1);
+ if (button >= 3)
+ button++;
+
+ /* Compute character position of mouse pointer */
+ row = (event->y - screen->border) / FontHeight(screen);
+ col = (event->x - OriginX(screen)) / FontWidth(screen);
+
+ /* Limit to screen dimensions */
+ if (row < 0)
+ row = 0;
+ else if (row > screen->max_row)
+ row = screen->max_row;
+
+ if (col < 0)
+ col = 0;
+ else if (col > screen->max_col)
+ col = screen->max_col;
+
+ if (mouse_limit > 0) {
+ /* Limit to representable mouse dimensions */
+ if (row > mouse_limit)
+ row = mouse_limit;
+ if (col > mouse_limit)
+ col = mouse_limit;
+ }
+
+ /* Build key sequence starting with \E[M */
+ if (screen->control_eight_bits) {
+ line[count++] = ANSI_CSI;
+ } else {
+ line[count++] = ANSI_ESC;
+ line[count++] = '[';
+ }
+ switch (screen->extend_coords) {
+ case 0:
+ case SET_EXT_MODE_MOUSE:
+#if OPT_SCO_FUNC_KEYS
+ if (xw->keyboard.type == keyboardIsSCO) {
+ /*
+ * SCO function key F1 is \E[M, which would conflict with xterm's
+ * normal kmous.
+ */
+ line[count++] = '>';
+ }
+#endif
+ line[count++] = final;
+ break;
+ case SET_SGR_EXT_MODE_MOUSE:
+ line[count++] = '<';
+ break;
+ }
+
+ /* Add event code to key sequence */
+ if (screen->send_mouse_pos == X10_MOUSE) {
+ count = EMIT_BUTTON(button);
+ } else {
+ /* Button-Motion events */
+ switch (event->type) {
+ case ButtonPress:
+ screen->mouse_button = button;
+ count = EMIT_BUTTON(button);
+ break;
+ case ButtonRelease:
+ /*
+ * Wheel mouse interface generates release-events for buttons
+ * 4 and 5, coded here as 3 and 4 respectively. We change the
+ * release for buttons 1..3 to a -1, which will be later mapped
+ * into a "0" (some button was released).
+ */
+ if (button < 3) {
+ switch (screen->extend_coords) {
+ case SET_SGR_EXT_MODE_MOUSE:
+ final = 'm';
+ break;
+ default:
+ button = -1;
+ break;
+ }
+ }
+ screen->mouse_button = button;
+ count = EMIT_BUTTON(button);
+ break;
+ case MotionNotify:
+ /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
+ * events only if character cell has changed.
+ */
+ if ((row == screen->mouse_row)
+ && (col == screen->mouse_col)) {
+ changed = False;
+ } else {
+ count = EMIT_BUTTON(screen->mouse_button);
+ }
+ break;
+ default:
+ changed = False;
+ break;
+ }
+ }
+
+ if (changed) {
+ screen->mouse_row = row;
+ screen->mouse_col = col;
+
+ TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
+
+ /* Add pointer position to key sequence */
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, col);
+ count = EmitMousePositionSeparator(screen, line, count);
+ count = EmitMousePosition(screen, line, count, row);
+
+ switch (screen->extend_coords) {
+ case SET_SGR_EXT_MODE_MOUSE:
+ case SET_URXVT_EXT_MODE_MOUSE:
+ line[count++] = final;
+ break;
+ }
+
+ /* Transmit key sequence to process running under xterm */
+ v_write(pty, line, count);
+ }
+ return;
+}
+
+#if OPT_FOCUS_EVENT
+void
+SendFocusButton(XtermWidget xw, XFocusChangeEvent * event)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (screen->send_focus_pos) {
+ ANSI reply;
+
+ memset(&reply, 0, sizeof(reply));
+ reply.a_type = ANSI_CSI;
+
+#if OPT_SCO_FUNC_KEYS
+ if (xw->keyboard.type == keyboardIsSCO) {
+ reply.a_pintro = '>';
+ }
+#endif
+ reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
+ unparseseq(xw, &reply);
+ }
+ return;
+}
+#endif /* OPT_FOCUS_EVENT */
+
+#if OPT_SELECTION_OPS
+/*
+ * Get the event-time, needed to process selections.
+ */
+static Time
+getEventTime(XEvent * event)
+{
+ Time result;
+
+ if (IsBtnEvent(event)) {
+ result = ((XButtonEvent *) event)->time;
+ } else if (IsKeyEvent(event)) {
+ result = ((XKeyEvent *) event)->time;
+ } else {
+ result = 0;
+ }
+
+ return result;
+}
+
+/* obtain the selection string, passing the endpoints to caller's parameters */
+static char *
+getSelectionString(XtermWidget xw,
+ Widget w,
+ XEvent * event,
+ String * params,
+ Cardinal *num_params,
+ CELL * start, CELL * finish)
+{
+ TScreen *screen = TScreenOf(xw);
+#if OPT_PASTE64
+ int base64_paste = (int) screen->base64_paste;
+#endif
+#if OPT_READLINE
+ int paste_brackets = (int) SCREEN_FLAG(screen, paste_brackets);
+#endif
+
+ /* override flags so that SelectionReceived only updates a buffer */
+#if OPT_PASTE64
+ screen->base64_paste = 0;
+#endif
+#if OPT_READLINE
+ SCREEN_FLAG_unset(screen, paste_brackets);
+#endif
+
+ screen->selectToBuffer = True;
+ screen->internal_select = 0;
+ xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
+ screen->selectToBuffer = False;
+
+ if (screen->internal_select != 0) {
+ TRACE(("getSelectionString %d:%s\n",
+ (int) strlen(screen->internal_select),
+ screen->internal_select));
+ *start = screen->startSel;
+ *finish = screen->endSel;
+ } else {
+ memset(start, 0, sizeof(*start));
+ memset(finish, 0, sizeof(*finish));
+ }
+#if OPT_PASTE64
+ screen->base64_paste = (Cardinal) base64_paste;
+#endif
+#if OPT_READLINE
+ if (paste_brackets)
+ SCREEN_FLAG_set(screen, paste_brackets);
+#endif
+ return screen->internal_select;
+}
+
+/* obtain data from the screen, passing the endpoints to caller's parameters */
+static char *
+getDataFromScreen(XtermWidget xw, String method, CELL * start, CELL * finish)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ CELL save_old_start = screen->startH;
+ CELL save_old_end = screen->endH;
+
+ CELL save_startSel = screen->startSel;
+ CELL save_startRaw = screen->startRaw;
+ CELL save_finishSel = screen->endSel;
+ CELL save_finishRaw = screen->endRaw;
+
+ int save_firstValidRow = screen->firstValidRow;
+ int save_lastValidRow = screen->lastValidRow;
+
+ SelectUnit saveUnits = screen->selectUnit;
+ SelectUnit saveMap = screen->selectMap[0];
+#if OPT_SELECT_REGEX
+ char *saveExpr = screen->selectExpr[0];
+#endif
+
+ Char *save_selection_data = screen->selection_data;
+ int save_selection_size = screen->selection_size;
+ unsigned long save_selection_length = screen->selection_length;
+
+ char *result = 0;
+
+ TRACE(("getDataFromScreen %s\n", method));
+
+ screen->selection_data = 0;
+ screen->selection_size = 0;
+ screen->selection_length = 0;
+
+ lookupSelectUnit(xw, 0, method);
+ screen->selectUnit = screen->selectMap[0];
+
+ start->row = screen->cur_row;
+ start->col = screen->cur_col;
+ *finish = *start;
+
+ ComputeSelect(xw, start, finish, False);
+ SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
+
+ if (screen->selection_length && screen->selection_data) {
+ TRACE(("...getDataFromScreen selection_data %.*s\n",
+ (int) screen->selection_length,
+ screen->selection_data));
+ result = malloc(screen->selection_length + 1);
+ if (result) {
+ memcpy(result, screen->selection_data, screen->selection_length);
+ result[screen->selection_length] = 0;
+ }
+ free(screen->selection_data);
+ }
+
+ TRACE(("...getDataFromScreen restoring previous selection\n"));
+
+ screen->startSel = save_startSel;
+ screen->startRaw = save_startRaw;
+ screen->endSel = save_finishSel;
+ screen->endRaw = save_finishRaw;
+
+ screen->firstValidRow = save_firstValidRow;
+ screen->lastValidRow = save_lastValidRow;
+
+ screen->selectUnit = saveUnits;
+ screen->selectMap[0] = saveMap;
+#if OPT_SELECT_REGEX
+ screen->selectExpr[0] = saveExpr;
+#endif
+
+ screen->selection_data = save_selection_data;
+ screen->selection_size = save_selection_size;
+ screen->selection_length = save_selection_length;
+
+ TrackText(xw, &save_old_start, &save_old_end);
+
+ TRACE(("...getDataFromScreen done\n"));
+ return result;
+}
+
+/*
+ * Split-up the format before substituting data, to avoid quoting issues.
+ * The resource mechanism has a limited ability to handle escapes. We take
+ * the result as if it were an sh-type string and parse it into a regular
+ * argv array.
+ */
+static char **
+tokenizeFormat(String format)
+{
+ char **result = 0;
+ int pass;
+ int argc;
+ int n;
+
+ format = x_skip_blanks(format);
+ if (*format != '\0') {
+ char *blob = x_strdup(format);
+
+ for (pass = 0; pass < 2; ++pass) {
+ int used = 0;
+ int first = 1;
+ int escaped = 0;
+ int squoted = 0;
+ int dquoted = 0;
+
+ argc = 0;
+ for (n = 0; format[n] != '\0'; ++n) {
+ if (escaped) {
+ blob[used++] = format[n];
+ escaped = 0;
+ } else if (format[n] == '"') {
+ if (!squoted) {
+ if (!dquoted)
+ blob[used++] = format[n];
+ dquoted = !dquoted;
+ }
+ } else if (format[n] == '\'') {
+ if (!dquoted) {
+ if (!squoted)
+ blob[used++] = format[n];
+ squoted = !squoted;
+ }
+ } else if (format[n] == '\\') {
+ blob[used++] = format[n];
+ escaped = 1;
+ } else {
+ if (first) {
+ first = 0;
+ if (pass) {
+ result[argc] = &blob[n];
+ }
+ ++argc;
+ }
+ if (isspace((Char) format[n])) {
+ first = !isspace((Char) format[n + 1]);
+ if (squoted || dquoted) {
+ blob[used++] = format[n];
+ } else if (first) {
+ blob[used++] = '\0';
+ }
+ } else {
+ blob[used++] = format[n];
+ }
+ }
+ }
+ blob[used] = '\0';
+ assert(strlen(blob) <= strlen(format));
+ if (!pass) {
+ result = TypeCallocN(char *, argc + 1);
+ if (result == 0) {
+ break;
+ }
+ }
+ }
+ }
+#if OPT_TRACE
+ if (result) {
+ TRACE(("tokenizeFormat %s\n", format));
+ for (argc = 0; result[argc]; ++argc) {
+ TRACE(("argv[%d] = %s\n", argc, result[argc]));
+ }
+ }
+#endif
+
+ return result;
+}
+
+static void
+formatVideoAttrs(XtermWidget xw, char *buffer, CELL * cell)
+{
+ TScreen *screen = TScreenOf(xw);
+ LineData *ld = GET_LINEDATA(screen, cell->row);
+
+ *buffer = '\0';
+ if (ld != 0 && cell->col < (int) ld->lineSize) {
+ Char attribs = ld->attribs[cell->col];
+ const char *delim = "";
+
+ if (attribs & INVERSE) {
+ buffer += sprintf(buffer, "7");
+ delim = ";";
+ }
+ if (attribs & UNDERLINE) {
+ buffer += sprintf(buffer, "%s4", delim);
+ delim = ";";
+ }
+ if (attribs & BOLD) {
+ buffer += sprintf(buffer, "%s1", delim);
+ delim = ";";
+ }
+ if (attribs & BLINK) {
+ buffer += sprintf(buffer, "%s5", delim);
+ delim = ";";
+ }
+#if OPT_ISO_COLORS
+ if (attribs & FG_COLOR) {
+ unsigned fg = extract_fg(xw, ld->color[cell->col], attribs);
+ if (fg < 8) {
+ fg += 30;
+ } else if (fg < 16) {
+ fg += 90;
+ } else {
+ buffer += sprintf(buffer, "%s38;5", delim);
+ delim = ";";
+ }
+ buffer += sprintf(buffer, "%s%u", delim, fg);
+ delim = ";";
+ }
+ if (attribs & BG_COLOR) {
+ unsigned bg = extract_bg(xw, ld->color[cell->col], attribs);
+ if (bg < 8) {
+ bg += 40;
+ } else if (bg < 16) {
+ bg += 100;
+ } else {
+ buffer += sprintf(buffer, "%s48;5", delim);
+ delim = ";";
+ }
+ (void) sprintf(buffer, "%s%u", delim, bg);
+ }
+#endif
+ }
+}
+
+/* substitute data into format, reallocating the result */
+static char *
+expandFormat(XtermWidget xw,
+ const char *format,
+ char *data,
+ CELL * start,
+ CELL * finish)
+{
+ char *result = 0;
+ if (!IsEmpty(format)) {
+ static char empty[1];
+ int pass;
+ int n;
+ char numbers[80];
+
+ if (data == 0)
+ data = empty;
+
+ for (pass = 0; pass < 2; ++pass) {
+ size_t need = 0;
+
+ for (n = 0; format[n] != '\0'; ++n) {
+ char *value = 0;
+
+ if (format[n] == '%') {
+ switch (format[++n]) {
+ case '%':
+ if (pass) {
+ result[need] = format[n];
+ }
+ ++need;
+ break;
+ case 'P':
+ sprintf(numbers, "%d;%d",
+ TScreenOf(xw)->topline + start->row + 1,
+ start->col + 1);
+ value = numbers;
+ break;
+ case 'p':
+ sprintf(numbers, "%d;%d",
+ TScreenOf(xw)->topline + finish->row + 1,
+ finish->col + 1);
+ value = numbers;
+ break;
+ case 'S':
+ sprintf(numbers, "%u", (unsigned) strlen(data));
+ value = numbers;
+ break;
+ case 's':
+ value = data;
+ break;
+ case 'T':
+ if ((value = x_strtrim(data)) != 0) {
+ sprintf(numbers, "%u", (unsigned) strlen(value));
+ free(value);
+ } else {
+ strcpy(numbers, "0");
+ }
+ value = numbers;
+ break;
+ case 't':
+ value = x_strtrim(data);
+ break;
+ case 'V':
+ formatVideoAttrs(xw, numbers, start);
+ value = numbers;
+ break;
+ case 'v':
+ formatVideoAttrs(xw, numbers, finish);
+ value = numbers;
+ break;
+ default:
+ if (pass) {
+ result[need] = format[n];
+ }
+ --n;
+ ++need;
+ break;
+ }
+ if (value != 0) {
+ if (pass) {
+ strcpy(result + need, value);
+ }
+ need += strlen(value);
+ if (value != numbers && value != data) {
+ free(value);
+ }
+ }
+ } else {
+ if (pass) {
+ result[need] = format[n];
+ }
+ ++need;
+ }
+ }
+ if (pass) {
+ result[need] = '\0';
+ } else {
+ ++need;
+ result = malloc(need);
+ if (result == 0) {
+ break;
+ }
+ }
+ }
+ }
+ TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
+ return result;
+}
+
+/* execute the command after forking. The main process frees its data */
+static void
+executeCommand(char **argv)
+{
+ if (fork() == 0) {
+ execvp(argv[0], argv);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+freeArgv(char *blob, char **argv)
+{
+ int n;
+
+ if (blob) {
+ free(blob);
+ if (argv) {
+ for (n = 0; argv[n]; ++n)
+ free(argv[n]);
+ free(argv);
+ }
+ }
+}
+
+void
+HandleExecFormatted(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params, /* selections */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleExecFormatted(%d)\n", *num_params));
+
+ if (*num_params > 1) {
+ CELL start, finish;
+ char *data;
+ char **argv;
+ char *blob;
+ int argc;
+
+ data = getSelectionString(xw, w, event, params, num_params,
+ &start, &finish);
+ argv = tokenizeFormat(params[0]);
+ blob = argv[0];
+ for (argc = 0; argv[argc] != 0; ++argc) {
+ argv[argc] = expandFormat(xw, argv[argc], data, &start, &finish);
+ }
+ executeCommand(argv);
+ freeArgv(blob, argv);
+ }
+ }
+}
+
+void
+HandleExecSelectable(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params, /* selections */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleExecSelectable(%d)\n", *num_params));
+
+ if (*num_params == 2) {
+ CELL start, finish;
+ char *data;
+ char **argv;
+ char *blob;
+ int argc;
+
+ data = getDataFromScreen(xw, params[1], &start, &finish);
+ argv = tokenizeFormat(params[0]);
+ blob = argv[0];
+ for (argc = 0; argv[argc] != 0; ++argc) {
+ argv[argc] = expandFormat(xw, argv[argc], data, &start, &finish);
+ }
+ executeCommand(argv);
+ freeArgv(blob, argv);
+ free(data);
+ }
+ }
+}
+
+void
+HandleInsertFormatted(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params, /* selections */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleInsertFormatted(%d)\n", *num_params));
+
+ if (*num_params > 1) {
+ CELL start, finish;
+ char *data;
+ char *temp = x_strdup(params[0]);
+
+ data = getSelectionString(xw, w, event, params, num_params,
+ &start, &finish);
+ temp = expandFormat(xw, temp, data, &start, &finish);
+ unparseputs(xw, temp);
+ free(data);
+ free(temp);
+ }
+ }
+}
+
+void
+HandleInsertSelectable(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params, /* selections */
+ Cardinal *num_params)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TRACE(("HandleInsertSelectable(%d)\n", *num_params));
+
+ if (*num_params == 2) {
+ CELL start, finish;
+ char *data;
+ char *temp = x_strdup(params[0]);
+
+ data = getDataFromScreen(xw, params[1], &start, &finish);
+ temp = expandFormat(xw, temp, data, &start, &finish);
+ unparseputs(xw, temp);
+ free(data);
+ free(temp);
+ }
+ }
+}
+#endif /* OPT_SELECTION_OPS */