xenocara/app/xterm/button.c

6168 lines
154 KiB
C

/* $XTermId: button.c,v 1.666 2024/09/30 07:44:57 tom Exp $ */
/*
* Copyright 1999-2023,2024 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 <charclass.h>
#include <xstrings.h>
#include <xterm_io.h>
#if OPT_SELECT_REGEX
#if defined(HAVE_PCRE2POSIX_H)
#include <pcre2posix.h>
/* pcre2 used to provide its "POSIX" entrypoints using the same names as the
* standard ones in the C runtime, but that never worked because the linker
* would use the C runtime. Debian patched the library to fix this symbol
* conflict, but overlooked the header file, and Debian's patch was made
* obsolete when pcre2 was changed early in 2019 to provide different names.
*
* Here is a workaround to make the older version of Debian's package work.
*/
#if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP)
#undef regcomp
#undef regexec
#undef regfree
#ifdef __cplusplus
extern "C" {
#endif
PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int);
PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t,
regmatch_t *, int);
PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *);
#ifdef __cplusplus
} /* extern "C" */
#endif
#define regcomp(r,s,n) PCRE2regcomp(r,s,n)
#define regexec(r,s,n,m,x) PCRE2regexec(r,s,n,m,x)
#define regfree(r) PCRE2regfree(r)
#endif
/* end workaround... */
#elif defined(HAVE_PCREPOSIX_H)
#include <pcreposix.h>
#else /* POSIX regex.h */
#include <sys/types.h>
#include <regex.h>
#endif
#endif /* OPT_SELECT_REGEX */
#ifdef HAVE_X11_TRANSLATEI_H
#include <X11/ConvertI.h>
#include <X11/TranslateI.h>
#else
extern String _XtPrintXlations(Widget w,
XtTranslations xlations,
Widget accelWidget,
_XtBoolean includeRHS);
#endif
#define PRIMARY_NAME "PRIMARY"
#define CLIPBOARD_NAME "CLIPBOARD"
#define SECONDARY_NAME "SECONDARY"
#define AtomToSelection(d,n) \
(((n) == XA_CLIPBOARD(d)) \
? CLIPBOARD_CODE \
: (((n) == XA_SECONDARY) \
? SECONDARY_CODE \
: PRIMARY_CODE))
#define isSelectionCode(n) ((n) >= PRIMARY_CODE)
#define CutBufferToCode(n) ((n) + MAX_SELECTION_CODES)
#define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE)
#if OPT_WIDE_CHARS
#include <ctype.h>
#include <wcwidth.h>
#else
#define CharacterClass(value) \
charClass[(value) & (int)((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))
#define MaxMouseBtn 5
#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
#define IsKeyEvent(event) ((event)->type == KeyPress || (event)->type == KeyRelease)
#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, Bool normal);
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, int which, 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 UnHiliteText(XtermWidget xw);
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:
case SET_PIXEL_POSITION_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:
case SET_URXVT_EXT_MODE_MOUSE:
case SET_PIXEL_POSITION_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:
case SET_PIXEL_POSITION_MOUSE:
line[count++] = ';';
break;
}
return count;
}
enum {
scanMods,
scanKey,
scanColon,
scanFunc,
scanArgs
};
#if OPT_TRACE > 1
static const char *
visibleScan(int mode)
{
const char *result = "?";
#define DATA(name) case name: result = #name; break
switch (mode) {
DATA(scanMods);
DATA(scanKey);
DATA(scanColon);
DATA(scanFunc);
DATA(scanArgs);
}
#undef DATA
return result;
}
#endif
#define L_BRACK '<'
#define R_BRACK '>'
#define L_PAREN '('
#define R_PAREN ')'
static char *
scanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
{
char *target = source;
*first = *last = 0;
if (IsEmpty(target)) {
target = 0;
} else {
do {
char ch;
while (IsSpace(*target))
target++;
*first = (unsigned) (target - source);
switch (*this_is = *next_is) {
case scanMods:
while ((ch = *target)) {
if (IsSpace(ch)) {
break;
} else if (ch == L_BRACK) {
*next_is = scanKey;
break;
} else if (ch == ':') {
*next_is = scanColon;
break;
} else if (ch == '~' && target != source) {
break;
}
target++;
}
break;
case scanKey:
while ((ch = *target)) {
if (IsSpace(ch)) {
break;
} else if (ch == ':') {
*next_is = scanColon;
break;
}
target++;
if (ch == R_BRACK)
break;
}
break;
case scanColon:
*next_is = scanFunc;
target++;
break;
case scanFunc:
while ((ch = *target)) {
if (IsSpace(ch)) {
break;
} else if (ch == L_PAREN) {
*next_is = scanArgs;
break;
}
target++;
}
break;
case scanArgs:
while ((ch = *target)) {
if (ch == R_PAREN) {
target++;
*next_is = scanFunc;
break;
}
target++;
}
break;
}
*last = (unsigned) (target - source);
if (*target == '\n') {
*next_is = scanMods;
target++;
}
} while (*first == *last);
}
return target;
}
void
xtermButtonInit(XtermWidget xw)
{
Widget w = (Widget) xw;
XErrorHandler save = XSetErrorHandler(ignore_x11_error);
XtTranslations xlations;
Widget xcelerat;
String result;
XtVaGetValues(w,
XtNtranslations, &xlations,
XtNaccelerators, &xcelerat,
(XtPointer) 0);
result = _XtPrintXlations(w, xlations, xcelerat, True);
if (result) {
static const char *table[] =
{
"insert-selection",
"select-end",
"select-extend",
"select-start",
"start-extend",
};
char *data = x_strdup(result);
char *next;
int state = scanMods;
int state2 = scanMods;
unsigned first;
unsigned last;
int have_button = -1;
Bool want_button = False;
Bool have_shift = False;
unsigned allowed = 0;
unsigned disallow = 0;
TRACE(("xtermButtonInit length %ld\n", (long) strlen(result)));
xw->keyboard.print_translations = data;
while ((next = scanTrans(data, &state, &state2, &first, &last)) != 0) {
unsigned len = (last - first);
TRACE2(("parse %s:%d..%d '%.*s'\n",
visibleScan(state), first, last,
len, data + first));
if (state == scanMods) {
if (len > 1 && data[first] == '~') {
len--;
first++;
}
if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) {
have_button = data[first + 6] - '0';
} else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) {
have_shift = True;
}
} else if (state == scanKey) {
if (!x_strncasecmp(data + first, "<buttonpress>", len) ||
!x_strncasecmp(data + first, "<buttonrelease>", len)) {
want_button = True;
} else if (want_button) {
have_button = data[first] - '0';
want_button = False;
}
} else if (state == scanFunc && have_button > 0) {
Cardinal n;
unsigned bmask = 1U << (have_button - 1);
for (n = 0; n < XtNumber(table); ++n) {
if (!x_strncasecmp(table[n], data + first, len)) {
TRACE(("...button %d: %s%s\n",
have_button, table[n],
have_shift ? " (disallow)" : ""));
if (have_shift)
disallow |= bmask;
else
allowed |= bmask;
break;
}
}
}
if (state2 == scanMods && state >= scanColon) {
have_button = -1;
want_button = False;
have_shift = False;
}
state = state2;
data = next;
}
XFree((char *) result);
xw->keyboard.shift_buttons = allowed & ~disallow;
#if OPT_TRACE
if (xw->keyboard.shift_buttons) {
int button = 0;
unsigned mask = xw->keyboard.shift_buttons;
TRACE(("...Buttons used for selection that can be overridden:"));
while (mask != 0) {
++button;
if ((mask & 1) != 0)
TRACE((" %d", button));
mask >>= 1;
}
TRACE(("\n"));
} else {
TRACE(("...No buttons used with selection can be overridden\n"));
}
#endif
}
XSetErrorHandler(save);
}
/*
* Shift and control are regular X11 modifiers, but meta is not:
* + X10 (which had no xmodmap utility) had a meta mask, but X11 did not.
* + X11R1 introduced xmodmap, along with the current set of modifier masks.
* The meta key has been assumed to be mod1 since X11R1.
* The initial xterm logic in X11 was different, but gave the same result.
* + X11R2 modified xterm was to eliminate the X10 table which provided part of
* the meta logic.
* + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and
* equating Alt with Meta. Neither Alt/Meta are modifiers, but Alt is more
* likely to be on the keyboard. This release also added keymap tables for
* the server; Meta was used frequently in HP keymaps, which were the most
* extensive set of keymaps.
* + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are
* found in the keysyms for a given modifier, that the client should use
* that modifier.
*
* This function follows the ICCCM, picking the modifier which contains the
* Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R
* (as per X11R3), and ultimately to mod1 (per X11R1).
*/
static unsigned
MetaMask(XtermWidget xw)
{
#if OPT_NUM_LOCK
unsigned meta = xw->work.meta_mods;
if (meta == 0)
meta = xw->work.alt_mods;
if (meta == 0)
meta = Mod1Mask;
#else
unsigned meta = Mod1Mask;
(void) xw;
#endif
return meta;
}
/*
* Returns a mask of the modifiers we may use for modifying the mouse protocol
* response strings.
*/
static unsigned
OurModifiers(XtermWidget xw)
{
return (ShiftMask
| ControlMask
| MetaMask(xw));
}
/*
* The actual check for the shift-mask, to see if it should tell xterm to
* override mouse-protocol in favor of select/paste actions depends upon
* whether the shiftEscape resource is set to true/always vs false/never.
*/
static Boolean
ShiftOverride(XtermWidget xw, unsigned state, int button)
{
unsigned check = (state & OurModifiers(xw));
Boolean result = False;
if (check & ShiftMask) {
if (xw->keyboard.shift_escape == ssFalse ||
xw->keyboard.shift_escape == ssNever) {
result = True;
} else if (xw->keyboard.shift_escape == ssTrue) {
/*
* Check if the button is one that we found does not directly use
* the shift-modifier in its bindings to select/copy actions.
*/
if (button > 0 && button <= MaxMouseBtn) {
if (xw->keyboard.shift_buttons & (1U << (button - 1))) {
result = True;
}
} else {
result = True; /* unlikely, and we don't care */
}
}
}
TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result));
return result;
}
/*
* Normally xterm treats the shift-modifier specially when the mouse protocol
* is active. The translations resource binds otherwise unmodified button
* for these mouse-related events:
*
* ~Meta <Btn1Down>:select-start() \n\
* ~Meta <Btn1Motion>:select-extend() \n\
* ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\
* ~Ctrl ~Meta <Btn3Down>:start-extend() \n\
* ~Meta <Btn3Motion>:select-extend() \n\
* <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\
*
* There is no API in the X libraries which would tell us if a given mouse
* button is bound to one of these actions. These functions make the choice
* configurable.
*/
static Bool
InterpretButton(XtermWidget xw, XButtonEvent *event)
{
Bool result = False;
if (ShiftOverride(xw, event->state, (int) event->button)) {
TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button));
result = True;
}
return result;
}
#define Button1Index 8 /* X.h should have done this */
static int
MotionButton(unsigned state)
{
unsigned bmask = state >> Button1Index;
int result = 1;
if (bmask != 0) {
while (!(bmask & 1)) {
++result;
bmask >>= 1;
}
}
return result;
}
static Bool
InterpretEvent(XtermWidget xw, XEvent *event)
{
Bool result = False; /* if not a button, is motion */
if (IsBtnEvent(event)) {
result = InterpretButton(xw, (XButtonEvent *) event);
} else if (event->type == MotionNotify) {
unsigned state = event->xmotion.state;
int button = MotionButton(state);
if (ShiftOverride(xw, state, button)) {
TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n",
button,
event->xmotion.y,
event->xmotion.x));
result = True;
}
}
return result;
}
#define OverrideEvent(event) InterpretEvent(xw, event)
#define OverrideButton(event) InterpretButton(xw, event)
/*
* Returns true if we handled the event here, and nothing more is needed.
*/
Bool
SendMousePosition(XtermWidget xw, XEvent *event)
{
XButtonEvent *my_event = (XButtonEvent *) event;
Bool result = False;
switch (okSendMousePos(xw)) {
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 (!OverrideEvent(event)) {
/* 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;
case X10_MOUSE: /* X10 compatibility sequences */
if (IsBtnEvent(event)) {
if (!OverrideButton(my_event)) {
if (my_event->type == ButtonPress)
EditorButton(xw, my_event);
result = True;
}
}
break;
case VT200_HIGHLIGHT_MOUSE: /* DEC vt200 hilite tracking */
if (IsBtnEvent(event)) {
if (!OverrideButton(my_event)) {
if (my_event->type == ButtonPress &&
my_event->button == Button1) {
TrackDown(xw, my_event);
} else {
EditorButton(xw, my_event);
}
result = True;
}
}
break;
case VT200_MOUSE: /* DEC vt200 compatible */
if (IsBtnEvent(event)) {
if (!OverrideButton(my_event)) {
EditorButton(xw, my_event);
result = True;
}
}
break;
case DEC_LOCATOR:
#if OPT_DEC_LOCATOR
if (IsBtnEvent(event) || event->type == MotionNotify) {
result = SendLocatorPosition(xw, my_event);
}
#endif /* OPT_DEC_LOCATOR */
break;
}
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)) {
if (OverrideButton(event))
return (False);
} else {
if (!screen->loc_filter)
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:
* bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
* 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 anyway.
*/
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) \
{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8); \
/* swap Button1 & Button3 */ \
(state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0); \
}
void
GetLocatorPosition(XtermWidget xw)
{
ANSI reply;
TScreen *screen = TScreenOf(xw);
Window root, child;
int rx, ry, x, y;
unsigned int mask = 0;
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 (okSendMousePos(xw) == 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;
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)) {
int state;
/* 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;
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 {
int state;
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(XtermWidget xw, XButtonEvent *event)
{
TScreen *screen = TScreenOf(xw);
int delta;
/* Disable on Shift-Click-1, including the application-mouse modes */
if (OverrideButton(event)
|| (okSendMousePos(xw) != MOUSE_OFF)
|| 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(XtermWidget xw, TScreen *screen, XButtonEvent *event)
{
int delta;
if (event->type != ButtonRelease
|| OverrideButton(event)
|| 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(XtermWidget xw, TScreen *screen, XEvent *event)
{
int delta;
if (event->type != ButtonPress
|| OverrideEvent(event)
|| 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;
*deltap = 0;
if (line != screen->cur_row) {
int l1, l2;
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(XtermWidget xw, int col, int ldelta)
{
TScreen *screen = TScreenOf(xw);
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++] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
}
line[count] = CharOf(col > 0 ? 'C' : 'D');
if (col < 0)
col = -col;
while (col--)
v_write(screen->respond, line, (size_t) 3);
return 1;
}
static int
ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
{
int del;
Char erases[2];
erases[0] = (Char) get_tty_erase(screen->respond, XTERM_ERASE, "pty");
erases[1] = 0;
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, erases, (size_t) 1);
return 1;
}
static void
readlineExtend(XtermWidget xw, XEvent *event)
{
TScreen *screen = TScreenOf(xw);
int ldelta1, ldelta2;
if (IsBtnEvent(event)) {
XButtonEvent *my_event = (XButtonEvent *) event;
if (isClick1_clean(xw, my_event)
&& SCREEN_FLAG(screen, click1_moves)
&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta1);
}
if (isDoubleClick3(xw, screen, my_event)
&& SCREEN_FLAG(screen, dclick3_deletes)
&& rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
&& rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
ReadLineMovePoint(xw, 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);
if (IsBtnEvent(event)
&& (event->xbutton.y >= screen->border)
&& (event->xbutton.x >= OriginX(screen))) {
Char Line[6];
unsigned line, col;
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, (size_t) 5);
}
}
}
#if OPT_READLINE
void
ReadLineButton(Widget w,
XEvent *event, /* must be XButtonEvent */
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TScreen *screen = TScreenOf(xw);
Char Line[6];
int line, col, ldelta = 0;
if (!IsBtnEvent(event)
|| (okSendMousePos(xw) != 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;
Line[1] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
Line[2] = CharOf(col > 0 ? 'C' : 'D');
if (col < 0)
col = -col;
while (col--)
v_write(screen->respond, Line, (size_t) 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;
if (IsBtnEvent(event)) {
int line;
line = screen->cur_row -
((event->xbutton.y - screen->border) / FontHeight(screen));
if (line != 0) {
Char Line[6];
Line[0] = ANSI_ESC; /* force an exit from insert-mode */
v_write(pty, Line, (size_t) 1);
if (line < 0) {
line = -line;
Line[0] = CONTROL('n');
} else {
Line[0] = CONTROL('p');
}
while (--line >= 0)
v_write(pty, Line, (size_t) 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_EVENT("HandleSelectExtend", event, params, num_params);
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 (okSendMousePos(xw) == BTN_EVENT_MOUSE
|| okSendMousePos(xw) == 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_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
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;
TRACE(("do_select_end %s @%ld\n",
visibleEventMode(screen->eventMode),
screen->selection_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(xw, 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);
}
}
void
HandlePointerMotion(Widget w,
XEvent *event,
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
(void) params;
(void) num_params;
if ((xw = getXtermWidget(w)) != 0) {
TRACE(("HandlePointerMotion\n"));
if (event->type == MotionNotify)
(void) SendMousePosition(xw, event);
}
}
void
HandlePointerButton(Widget w,
XEvent *event,
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
(void) params;
(void) num_params;
if ((xw = getXtermWidget(w)) != 0) {
TRACE(("HandlePointerButton\n"));
if (IsBtnEvent(event))
(void) SendMousePosition(xw, event);
}
}
/*
* 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_EVENT("HandleCopySelection", event, params, num_params);
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 */
} else {
ch = '?'; /* DEC Technical has no mapping */
}
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;
if (len != 0) {
PtyData data;
Boolean save_vt100 = screen->vt100_graphics;
fakePtyData(&data, s, s + len);
screen->vt100_graphics = False; /* temporary override */
while (decodeUtf8(screen, &data)) {
Bool fails = False;
Bool extra = False;
IChar value;
skipPtyData(&data, value);
if (is_UCS_SPECIAL(value)) {
fails = True;
} else if (value < 256) {
AddChar(&buffer, &used, offset, CharOf(value));
} else {
unsigned eqv = ucs2dec(screen, value);
if (xtermIsInternalCs(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) {
const Char *p;
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');
screen->vt100_graphics = save_vt100;
*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;
if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
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';
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);
char *item = x_strtrim(listp);
size_t len = (item ? strlen(item) : 0);
if (len == 0) {
/* EMPTY */ ;
}
#if OPT_WIDE_CHARS
else if (sameItem(item, "UTF8")) {
result[count++] = XA_UTF8_STRING(XtDisplay(w));
}
#endif
else if (sameItem(item, "I18N")) {
if (screen->i18nSelections) {
result[count++] = XA_TEXT(XtDisplay(w));
result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
}
} else if (sameItem(item, "TEXT")) {
result[count++] = XA_TEXT(XtDisplay(w));
} else if (sameItem(item, "COMPOUND_TEXT")) {
result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
} else if (sameItem(item, "STRING")) {
result[count++] = XA_STRING;
}
*nextp++ = nextc;
listp = nextp;
free(item);
} while (nextc != '\0');
if (count) {
result[count] = None;
override = True;
*resultp = result;
} else {
XtFree((char *) result);
}
}
free(copied);
} 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;
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0) {
result = NULL;
} else {
TScreen *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(NonNull(value), "SELECT"))
static int
DefaultSelection(TScreen *screen)
{
return (screen->selectToClipboard ? 1 : 0);
}
static int
TargetToSelection(TScreen *screen, String name)
{
int result = -1;
int cutb;
if (isSELECT(name)) {
result = DefaultSelection(screen);
} else if (!strcmp(name, PRIMARY_NAME)) {
result = PRIMARY_CODE;
} else if (!strcmp(name, CLIPBOARD_NAME)) {
result = CLIPBOARD_CODE;
} else if (!strcmp(name, SECONDARY_NAME)) {
result = SECONDARY_CODE;
} else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
result = CutBufferToCode(cutb);
} else {
xtermWarning("unexpected cut-buffer code: %d\n", cutb);
}
} else {
xtermWarning("unexpected selection target: %s\n", name);
}
TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
return result;
}
void
UnmapSelections(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
FreeAndNull(screen->mappedSelect);
}
/*
* 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 (params != 0 && 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_NAME
: PRIMARY_NAME);
UnmapSelections(xw);
if ((result = TypeMallocN(String, num_params + 1)) != 0) {
result[num_params] = 0;
for (j = 0; j < num_params; ++j) {
result[j] = (String) (isSELECT(params[j])
? mapTo
: params[j]);
if (result[j] == 0) {
UnmapSelections(xw);
FreeAndNull(result);
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 a type of selection, e.g., primary.
*/
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;
}
TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
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 @%ld\n", num_params, ev_time));
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
} else {
if (targets == NULL || targets[0] == None) {
targets = _SelectionTargets(w);
}
if (targets != 0) {
struct _SelectionList *list;
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;
const char *name = TraceAtomName(dpy, type);
TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
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
#define tty_vwrite(pty,lag,l) v_write(pty,lag,(size_t) l)
#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;
TRACE(("base64_flush count %d, pad %d (%d)\n",
screen->base64_count,
screen->base64_pad,
screen->base64_pad & 3));
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) (3 - (screen->base64_pad & 3)));
}
screen->base64_count = 0;
screen->base64_accu = 0;
screen->base64_pad = 0;
}
#endif /* OPT_PASTE64 */
/*
* Translate ISO-8859-1 or UTF-8 data to NRCS.
*/
static void
ToNational(XtermWidget xw, Char *buffer, size_t *length)
{
TScreen *screen = TScreenOf(xw);
DECNRCM_codes gsetL = screen->gsets[screen->curgl];
DECNRCM_codes gsetR = screen->gsets[screen->curgr];
#if OPT_WIDE_CHARS
if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
Char *p;
PtyData *data = TypeXtMallocX(PtyData, *length);
memset(data, 0, sizeof(*data));
data->next = data->buffer;
data->last = data->buffer + *length;
memcpy(data->buffer, buffer, *length);
p = buffer;
while (data->next < data->last) {
unsigned chr, out, gl, gr;
if (!decodeUtf8(screen, data)) {
data->utf_size = 1;
data->utf_data = data->next[0];
}
data->next += data->utf_size;
chr = data->utf_data;
out = chr;
if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
out = gl;
} else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
out = gr;
}
*p++ = (Char) ((out < 256) ? out : ' ');
}
*length = (size_t) (p - buffer);
free(data);
} else
#endif
{
Char *p;
for (p = buffer; (size_t) (p - buffer) < *length; ++p) {
unsigned gl, gr;
unsigned chr = *p;
unsigned out = chr;
if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
out = gl;
} else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
out = gr;
}
*p = (Char) out;
}
}
}
static void
_qWriteSelectionData(XtermWidget xw, Char *lag, size_t length)
{
TScreen *screen = TScreenOf(xw);
/*
* If we are pasting into a window which is using NRCS, we want to map
* the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
* that an application would use to write characters with NRCS.
*
* TODO: handle conversion from UTF-8, and adjust length. This can be done
* in the same buffer because the target is always 8-bit.
*/
if ((xw->flags & NATIONAL) && (length != 0)) {
ToNational(xw, lag, &length);
}
#if OPT_PASTE64
if (screen->base64_paste) {
/* Send data as base64 */
Char *p = lag;
Char buf[64];
unsigned x = 0;
TRACE(("convert to base64 %lu:%s\n",
(unsigned long) length,
visibleChars(p, length)));
/*
* Handle the case where the selection is from _this_ xterm, which
* puts part of the reply in the buffer before the selection callback
* happens.
*/
if (screen->base64_paste && screen->unparse_len) {
unparse_end(xw);
}
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;
TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
tty_vwrite(screen->respond, buf, x);
x = 0;
}
}
if (x != 0) {
screen->base64_pad += x;
TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
tty_vwrite(screen->respond, buf, x);
}
} else
#endif /* OPT_PASTE64 */
#if OPT_READLINE
if (SCREEN_FLAG(screen, paste_quotes)) {
Char quote[2];
quote[0] = (Char) get_tty_lnext(screen->respond, XTERM_LNEXT, "pty");
quote[1] = 0;
TRACE(("writing quoted selection data %s\n", visibleChars(lag, length)));
while (length--) {
tty_vwrite(screen->respond, quote, 1);
tty_vwrite(screen->respond, lag++, 1);
}
} else
#endif
{
TRACE(("writing selection data %s\n", visibleChars(lag, length)));
tty_vwrite(screen->respond, lag, length);
}
}
static void
_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
{
#if OPT_PASTE64 || OPT_READLINE
TScreen *screen = TScreenOf(xw);
#endif
#if OPT_PASTE64
if (screen->base64_paste) {
_qWriteSelectionData(xw, line, length);
base64_flush(screen);
} else
#endif
{
if (!SCREEN_FLAG(screen, paste_literal_nl)) {
size_t n;
for (n = 0; n < length; ++n) {
if (line[n] == '\n') {
line[n] = '\r';
}
}
}
_qWriteSelectionData(xw, line, length);
}
}
#if OPT_PASTE64 || 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 */
/*
* Unless enabled by the user, strip control characters other than formatting.
*/
static size_t
removeControls(XtermWidget xw, char *value)
{
TScreen *screen = TScreenOf(xw);
size_t dst = 0;
if (screen->allowPasteControls) {
dst = strlen(value);
} else {
size_t src = 0;
Boolean *disallowed = screen->disallow_paste_ops;
TERMIO_STRUCT data;
char current_chars[epLAST];
if (disallowed[epSTTY] && ttyGetAttr(screen->respond, &data) == 0) {
int n;
int disabled = xtermDisabledChar();
TRACE(("disallow(STTY):"));
memcpy(current_chars, disallowed, sizeof(current_chars));
for (n = 0; n < NCCS; ++n) {
PasteControls nc = (data.c_cc[n] < 32
? data.c_cc[n]
: (data.c_cc[n] == 127
? epDEL
: epLAST));
if (nc == epNUL || nc == epLAST)
continue;
if (CharOf(data.c_cc[n]) == CharOf(disabled))
continue;
if ((n == VMIN || n == VTIME) && !(data.c_lflag & ICANON))
continue;
switch (n) {
/* POSIX */
case VEOF:
case VEOL:
case VERASE:
case VINTR:
case VKILL:
case VQUIT:
case VSTART:
case VSTOP:
case VSUSP:
/* system-dependent */
#ifdef VDISCARD
case VDISCARD:
#endif
#ifdef VDSUSP
case VDSUSP:
#endif
#ifdef VEOL2
case VEOL2:
#endif
#ifdef VLNEXT
case VLNEXT:
#endif
#ifdef VREPRINT
case VREPRINT:
#endif
#ifdef VSTATUS
case VSTATUS:
#endif
#ifdef VSWTC
case VSWTC: /* System V SWTCH */
#endif
#ifdef VWERASE
case VWERASE:
#endif
break;
default:
continue;
}
if (nc != epLAST) {
TRACE((" \\%03o", data.c_cc[n]));
current_chars[nc] = 1;
}
}
TRACE(("\n"));
disallowed = current_chars;
}
while ((value[dst] = value[src]) != '\0') {
int ch = CharOf(value[src++]);
#define ReplacePaste(n) \
if (disallowed[n]) \
value[dst] = ' '
if (ch < 32) {
ReplacePaste(epC0);
ReplacePaste(ch);
++dst;
} else if (ch == ANSI_DEL) {
ReplacePaste(epDEL);
++dst;
}
#if OPT_WIDE_CHARS
else if (screen->utf8_inparse || screen->utf8_nrc_mode)
++dst;
#endif
#if OPT_C1_PRINT || OPT_WIDE_CHARS
else if (screen->c1_printable)
++dst;
#endif
else if (ch >= 128 && ch < 160)
continue;
else
++dst;
}
}
return dst;
}
#if OPT_SELECTION_OPS
static void
beginInternalSelect(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
InternalSelect *mydata = &(screen->internal_select);
(void) mydata;
/* override flags so that SelectionReceived only updates a buffer */
#if OPT_PASTE64
mydata->base64_paste = screen->base64_paste;
screen->base64_paste = 0;
#endif
#if OPT_PASTE64 || OPT_READLINE
mydata->paste_brackets = screen->paste_brackets;
SCREEN_FLAG_unset(screen, paste_brackets);
#endif
}
static void
finishInternalSelect(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
InternalSelect *mydata = &(screen->internal_select);
(void) mydata;
#if OPT_PASTE64
screen->base64_paste = mydata->base64_paste;
#endif
#if OPT_PASTE64 || OPT_READLINE
screen->paste_brackets = mydata->paste_brackets;
#endif
}
#else
#define finishInternalSelect(xw) /* nothing */
#endif /* OPT_SELECTION_OPS */
/* 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 = 0;
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) {
TRACE(("...no data to convert\n"));
goto fail;
}
text_prop.value = (unsigned char *) value;
text_prop.encoding = *type;
text_prop.format = *format;
text_prop.nitems = *length;
TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
TraceAtomName(screen->display, *selection),
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_PASTE64 || OPT_READLINE
if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
_WriteKey(screen, (const Char *) "200");
}
#endif
for (i = 0; i < text_list_count; i++) {
size_t len = removeControls(xw, text_list[i]);
if (screen->selectToBuffer) {
InternalSelect *mydata = &(screen->internal_select);
if (!mydata->done) {
size_t have = (mydata->buffer
? strlen(mydata->buffer)
: 0);
size_t need = have + len + 1;
char *buffer = realloc(mydata->buffer, need);
if (buffer != 0) {
strcpy(buffer + have, text_list[i]);
mydata->buffer = buffer;
}
TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
screen->startSel.row,
screen->startSel.col,
screen->endSel.row,
screen->endSel.col,
mydata->buffer));
mydata->format_select(w, mydata->format, mydata->buffer,
&(screen->startSel),
&(screen->endSel));
mydata->done = True;
}
} else {
_WriteSelectionData(xw, (Char *) text_list[i], len);
}
}
#if OPT_PASTE64
if (screen->base64_paste) {
FinishPaste64(xw);
} else
#endif
#if OPT_PASTE64 || OPT_READLINE
if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
_WriteKey(screen, (const Char *) "201");
}
#endif
if (screen->selectToBuffer) {
InternalSelect *mydata = &(screen->internal_select);
finishInternalSelect(xw);
if (mydata->done) {
free(mydata->format);
free(mydata->buffer);
memset(mydata, 0, sizeof(*mydata));
}
screen->selectToBuffer = False;
}
XFreeStringList(text_list);
} else {
TRACE(("...empty text-list\n"));
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_EVENT("HandleInsertSelection", event, params, num_params);
if (!SendMousePosition(xw, event)) {
#if OPT_READLINE
int ldelta;
TScreen *screen = TScreenOf(xw);
if (IsBtnEvent(event)
&& !OverrideEvent(event)
&& (okSendMousePos(xw) == MOUSE_OFF)
&& SCREEN_FLAG(screen, paste_moves)
&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
ReadLineMovePoint(xw, 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_EVENT("HandleSelectStart", event, params, num_params);
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_EVENT("HandleKeyboardSelectStart", event, params, num_params);
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(xw);
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, True);
}
static void
EndExtend(XtermWidget xw,
XEvent *event, /* must be XButtonEvent */
String *params, /* selections */
Cardinal num_params,
Bool use_cursor_loc)
{
CELL cell;
TScreen *screen = TScreenOf(xw);
TRACE_EVENT("EndExtend", event, params, &num_params);
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) {
Char line[64];
unsigned 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:
case SET_PIXEL_POSITION_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:
case SET_PIXEL_POSITION_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:
case SET_PIXEL_POSITION_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:
case SET_PIXEL_POSITION_MOUSE:
line[count++] = 'T';
break;
}
}
v_write(screen->respond, line, (size_t) count);
UnHiliteText(xw);
}
}
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_EVENT("HandleSelectSet", event, params, num_params);
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))) {
Cardinal n;
for (n = 0; n < num_params; ++n) {
SaltTextAway(xw,
TargetToSelection(screen, params[n]),
&(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 (OverrideEvent(event)
|| 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(xw, screen, event);
#endif
if (screen->numberOfClicks == 1
|| (SCREEN_FLAG(screen, dclick3_deletes)
&& !OverrideEvent(event))) {
/* 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, 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, True);
#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_EVENT("HandleStartExtend", event, params, num_params);
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_EVENT("HandleKeyboardStartExtend", event, params, num_params);
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, 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
#define isWideCell(row, col) isWideFrg((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, CLineData *ld, int row)
{
int i = -1;
if (ld != 0) {
if (okScrnRow(screen, row)) {
const IAttr *ch;
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;
int result = 0;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(GetLineDblCS(ld))) {
temp.col /= 2;
}
#endif
if (temp.col < (int) ld->lineSize)
result = CharacterClass((int) (ld->charData[temp.col]));
return result;
}
#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 OPT_REPORT_CCLASS
static int
show_cclass_range(int lo, int hi)
{
int cclass = CharacterClass(lo);
int ident = (cclass == lo);
int more = 0;
if (ident) {
int ch;
for (ch = lo + 1; ch <= hi; ch++) {
if (CharacterClass(ch) != ch) {
ident = 0;
break;
}
}
if (ident && (hi < 255)) {
ch = hi + 1;
if (CharacterClass(ch) == ch) {
if (ch >= 255 || CharacterClass(ch + 1) != ch) {
more = 1;
}
}
}
}
if (!more) {
if (lo == hi) {
printf("\t%d", lo);
} else {
printf("\t%d-%d", lo, hi);
}
if (!ident)
printf(":%d", cclass);
if (hi < 255)
printf(", \\");
printf("\n");
}
return !more;
}
void
report_char_class(XtermWidget xw)
{
/* simple table, to match documentation */
static const char charnames[] =
"NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
" BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
"DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
"CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
" SP\0" " !\0" " \"\0" " #\0" " $\0" " %\0" " &\0" " '\0"
" (\0" " )\0" " *\0" " +\0" " ,\0" " -\0" " .\0" " /\0"
" 0\0" " 1\0" " 2\0" " 3\0" " 4\0" " 5\0" " 6\0" " 7\0"
" 8\0" " 9\0" " :\0" " ;\0" " <\0" " =\0" " >\0" " ?\0"
" @\0" " A\0" " B\0" " C\0" " D\0" " E\0" " F\0" " G\0"
" H\0" " I\0" " J\0" " K\0" " L\0" " M\0" " N\0" " O\0"
" P\0" " Q\0" " R\0" " S\0" " T\0" " U\0" " V\0" " W\0"
" X\0" " Y\0" " Z\0" " [\0" " \\\0" " ]\0" " ^\0" " _\0"
" `\0" " a\0" " b\0" " c\0" " d\0" " e\0" " f\0" " g\0"
" h\0" " i\0" " j\0" " k\0" " l\0" " m\0" " n\0" " o\0"
" p\0" " q\0" " r\0" " s\0" " t\0" " u\0" " v\0" " w\0"
" x\0" " y\0" " z\0" " {\0" " |\0" " }\0" " ~\0" "DEL\0"
"x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
"HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
"DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
"x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
" -\0" " i\0" " c/\0" " L\0" " ox\0" " Y-\0" " |\0" " So\0"
" ..\0" " c0\0" " ip\0" " <<\0" " _\0" " \0" " R0\0" " -\0"
" o\0" " +-\0" " 2\0" " 3\0" " '\0" " u\0" " q|\0" " .\0"
" ,\0" " 1\0" " 2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" " ?\0"
" A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
" E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
" D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" " X\0"
" O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" " P\0" " B\0"
" a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
" e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
" d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
" o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" " P\0" " y:\0";
int ch, dh;
int class_p;
(void) xw;
printf("static int charClass[256] = {\n");
for (ch = 0; ch < 256; ++ch) {
const char *s = charnames + (ch * 4);
if ((ch & 7) == 0)
printf("/*");
printf(" %s ", s);
if (((ch + 1) & 7) == 0) {
printf("*/\n ");
for (dh = ch - 7; dh <= ch; ++dh) {
printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
}
printf("\n");
}
}
/* print the table as if it were the charClass resource */
printf("\n");
printf("The table is equivalent to this \"charClass\" resource:\n");
class_p = CharacterClass(dh = 0);
for (ch = 0; ch < 256; ++ch) {
int class_c = CharacterClass(ch);
if (class_c != class_p) {
if (show_cclass_range(dh, ch - 1)) {
dh = ch;
class_p = class_c;
}
}
}
if (dh < 255) {
show_cclass_range(dh, 255);
}
if_OPT_WIDE_CHARS(TScreenOf(xw), {
/* if this is a wide-character configuration, print all intervals */
report_wide_char_class();
});
}
#endif
/*
* 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;
assert(ld != NULL);
assert(*ld != NULL);
if (*ld == NULL) {
result = False;
TRACE(("okPosition LineData is null!\n"));
} else if (cell->row > screen->max_row) {
result = False;
TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
} else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
(LastTextCol(screen, *ld, cell->row) + 1)));
if (cell->row < screen->max_row) {
TRACE(("okPosition cell row %d < screen max %d\n", 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) {
CLineData *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)
{
CLineData *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;
TRACE(("Select_REGEX[%d]:%s\n", inx, 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);
int *indexed;
TRACE(("regcomp ok rows %d..%d bytes %d\n",
firstRow, lastRow, size));
if ((indexed = TypeCallocN(int, size + 1)) != 0) {
char *search;
if ((search = make_indexed_text(screen,
firstRow,
size,
indexed)) != 0) {
int len = (int) strlen(search);
int col;
int offset;
int best_col = -1;
int best_len = -1;
startc->row = 0;
startc->col = 0;
endc->row = 0;
endc->col = 0;
for (col = 0; (offset = indexed[col]) < len; ++col) {
if (regexec(&preg,
search + offset,
(size_t) 1, &match,
col ? REG_NOTBOL : 0) == 0) {
int start_inx = (int) (match.rm_so + offset);
int finis_inx = (int) (match.rm_eo + offset);
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] -
indexed[best_col],
visibleChars((Char *) (search + indexed[best_col]),
(unsigned) (indexed[best_nxt] -
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,
Bool normal)
{
TScreen *screen = TScreenOf(xw);
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))) {
CELL mark;
cclass = CClassOf(startSel);
TRACE(("...starting with class %d\n", cclass));
do {
mark = screen->startSel;
--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));
if (normal)
++screen->startSel.col;
else
screen->startSel = mark;
}
#if OPT_WIDE_CHARS
#define SkipHiddenCell(mark) \
if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
mark.col++
#else
#define SkipHiddenCell(mark) /* nothing */
#endif
SkipHiddenCell(screen->startSel);
if (!normal) {
screen->endSel = screen->startSel;
ld.endSel = ld.startSel;
}
if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
cclass = CClassOf(endSel);
TRACE(("...ending with class %d\n", cclass));
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));
if (normal
&& screen->endSel.col > length + 1
&& MoreRows(endSel)) {
screen->endSel.col = 0;
NextRow(endSel);
}
}
SkipHiddenCell(screen->endSel);
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;
}
static void
UnHiliteText(XtermWidget xw)
{
TrackText(xw, &zeroCELL, &zeroCELL);
}
/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
static void
ReHiliteText(XtermWidget xw,
CELL *firstp,
CELL *lastp)
{
TScreen *screen = TScreenOf(xw);
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 */
int i;
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,
int which,
CELL *cellc,
CELL *cell)
{
TScreen *screen = TScreenOf(xw);
SelectedCells *scp;
int i;
int eol;
int need = 0;
size_t have = 0;
Char *line;
Char *lp;
CELL first = *cellc;
CELL last = *cell;
if (which < 0 || which >= MAX_SELECTIONS) {
TRACE(("SaltTextAway - which selection?\n"));
return;
}
scp = &(screen->selected_cells[which]);
TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
which, first.row, first.col, last.row, last.col));
if (isSameRow(&first, &last) && first.col > last.col) {
int tmp;
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)) {
need = Length(screen, first.row, first.col, last.col);
} else { /* two cases, cut is on same line, cut spans multiple lines */
need += Length(screen, first.row, first.col, screen->max_col) + 1;
for (i = first.row + 1; i < last.row; i++)
need += Length(screen, i, 0, screen->max_col) + 1;
if (last.col >= 0)
need += Length(screen, last.row, 0, last.col);
}
/* UTF-8 may require more space */
if_OPT_WIDE_CHARS(screen, {
if (need > 0) {
if (screen->max_combining > 0)
need += screen->max_combining;
need *= 6;
}
});
/* now get some memory to save it in */
if (need < 0)
return;
if (scp->data_limit <= (unsigned) need) {
if ((line = (Char *) malloc((size_t) need + 1)) == 0)
SysError(ERROR_BMALLOC2);
free(scp->data_buffer);
scp->data_buffer = line;
scp->data_limit = (size_t) (need + 1);
} else {
line = scp->data_buffer;
}
if (line == 0)
return;
line[need] = '\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 */
have = (size_t) (lp - line);
/*
* Scanning the buffer twice is unnecessary. Discard unwanted memory if
* the estimate is too-far off.
*/
if ((have * 2) < (size_t) need) {
Char *next;
scp->data_limit = have + 1;
next = realloc(line, scp->data_limit);
if (next == NULL) {
free(line);
scp->data_length = 0;
scp->data_limit = 0;
}
scp->data_buffer = next;
}
scp->data_length = have;
TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
visibleChars(scp->data_buffer, (unsigned) have)));
}
#if OPT_PASTE64
void
ClearSelectionBuffer(TScreen *screen, String selection)
{
int which = TargetToSelection(screen, selection);
SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
FreeAndNull(scp->data_buffer);
scp->data_limit = 0;
scp->data_length = 0;
screen->base64_count = 0;
}
static void
AppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
{
if (len != 0) {
size_t j = (scp->data_length + len);
size_t k = j + (j >> 2) + 80;
if (j + 1 >= scp->data_limit) {
Char *line;
if (!scp->data_length) {
line = (Char *) malloc(k);
} else {
line = (Char *) realloc(scp->data_buffer, k);
}
if (line == 0)
SysError(ERROR_BMALLOC2);
scp->data_buffer = line;
scp->data_limit = k;
}
if (scp->data_buffer != 0) {
memcpy(scp->data_buffer + scp->data_length, text, len);
scp->data_length += len;
scp->data_buffer[scp->data_length] = 0;
}
}
}
void
AppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
{
int which = TargetToSelection(screen, selection);
SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
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(scp, &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(scp, &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(scp, &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,
SelectedCells * scp,
Atom *type,
XtPointer *value,
unsigned long *length,
int *format,
int (*conversion_function) (Display *,
char **, int,
XICCEncodingStyle,
XTextProperty *),
XICCEncodingStyle conversion_style)
{
*value = 0;
*length = 0;
*type = 0;
*format = 0;
if (getXtermWidget(w) != 0) {
Display *dpy = XtDisplay(w);
XTextProperty textprop;
int out_n = 0;
char *result = 0;
char *the_data = (char *) scp->data_buffer;
char *the_next;
unsigned long remaining = scp->data_length;
TRACE(("converting %ld:'%s'\n",
(long) scp->data_length,
visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
/*
* For most selections, we can convert in one pass. It is possible
* that some applications contain embedded nulls, e.g., using xterm's
* paste64 feature. For those cases, we will build up the result in
* parts.
*/
if (memchr(the_data, 0, scp->data_length) != 0) {
TRACE(("selection contains embedded nulls\n"));
result = calloc(scp->data_length + 1, sizeof(char));
}
next_try:
memset(&textprop, 0, sizeof(textprop));
if (conversion_function(dpy, &the_data, 1,
conversion_style,
&textprop) >= Success) {
if ((result != 0)
&& (textprop.value != 0)
&& (textprop.format == 8)) {
char *text_values = (char *) textprop.value;
unsigned long in_n;
if (out_n == 0) {
*value = result;
*type = textprop.encoding;
*format = textprop.format;
}
for (in_n = 0; in_n < textprop.nitems; ++in_n) {
result[out_n++] = text_values[in_n];
}
*length += textprop.nitems;
if ((the_next = memchr(the_data, 0, remaining)) != 0) {
unsigned long this_was = (unsigned long) (the_next - the_data);
this_was++;
the_data += this_was;
remaining -= this_was;
result[out_n++] = 0;
*length += 1;
if (remaining)
goto next_try;
}
return True;
} else {
free(result);
*value = (XtPointer) textprop.value;
*length = textprop.nitems;
*type = textprop.encoding;
*format = textprop.format;
return True;
}
}
free(result);
}
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;
}
#define keepClipboard(d,atom) ((screen->keepClipboard) && \
(atom == XA_CLIPBOARD(d)))
static Boolean
ConvertSelection(Widget w,
Atom *selection,
Atom *target,
Atom *type,
XtPointer *value,
unsigned long *length,
int *format)
{
Display *dpy = XtDisplay(w);
TScreen *screen;
SelectedCells *scp;
Bool result = False;
Char *data;
unsigned long data_length;
XtermWidget xw;
if ((xw = getXtermWidget(w)) == 0)
return False;
screen = TScreenOf(xw);
TRACE(("ConvertSelection %s -> %s\n",
TraceAtomName(screen->display, *selection),
visibleSelectionTarget(dpy, *target)));
if (keepClipboard(dpy, *selection)) {
TRACE(("asked for clipboard\n"));
scp = &(screen->clipboard_data);
} else {
TRACE(("asked for selection\n"));
scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
}
data = scp->data_buffer;
data_length = scp->data_length;
if (data == NULL) {
TRACE(("...no selection-data\n"));
return False;
}
if (*target == XA_TARGETS(dpy)) {
Atom *targetP;
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);
Atom *allocP;
Atom *std_targets;
TRACE(("XmuConvertStandardSelection - success\n"));
std_targets = (Atom *) (void *) (std_return);
*length = std_length + 6;
targetP = TypeXtMallocN(Atom, *length);
allocP = targetP;
*value = (XtPointer) targetP;
if (my_targets != 0) {
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, scp,
type, value, length, format,
Xutf8TextListToTextProperty,
XStringStyle);
TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
} else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
result =
_ConvertSelectionHelper(w, scp,
type, value, length, format,
Xutf8TextListToTextProperty,
XUTF8StringStyle);
TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
} else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
result =
_ConvertSelectionHelper(w, scp,
type, value, length, format,
Xutf8TextListToTextProperty,
XStdICCTextStyle);
TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
} else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
result =
_ConvertSelectionHelper(w, scp,
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) data;
*length = data_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, scp,
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, scp,
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, scp,
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, scp->data_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);
TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
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)
UnHiliteText(xw);
}
/* 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 */
TRACE(("SelectionDone\n"));
}
static void
_OwnSelection(XtermWidget xw,
String *selections,
Cardinal count)
{
TScreen *screen = TScreenOf(xw);
Display *dpy = screen->display;
Atom *atoms = screen->selection_atoms;
Cardinal i;
Bool have_selection = False;
SelectedCells *scp;
if (count == 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(dpy, selections, count, atoms);
for (i = 0; i < count; i++) {
int cutbuffer = CutBuffer(atoms[i]);
if (cutbuffer >= 0) {
unsigned long limit =
(unsigned long) (4 * XMaxRequestSize(dpy) - 32);
scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
if (scp->data_length > limit) {
TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
(unsigned long) scp->data_length, cutbuffer));
xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
(unsigned long) scp->data_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 = scp->data_length;
Char *data = scp->data_buffer;
if_OPT_WIDE_CHARS((screen), {
data = UTF8toLatin1(screen, data, length, &length);
});
TRACE(("XStoreBuffer(%d)\n", cutbuffer));
XStoreBuffer(dpy,
(char *) data,
(int) length,
cutbuffer);
}
} else {
int which = AtomToSelection(dpy, atoms[i]);
if (keepClipboard(dpy, atoms[i])) {
Char *buf;
SelectedCells *tcp = &(screen->clipboard_data);
TRACE(("saving selection to clipboard buffer\n"));
scp = &(screen->selected_cells[CLIPBOARD_CODE]);
if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0) {
SysError(ERROR_BMALLOC2);
} else {
free(tcp->data_buffer);
memcpy(buf, scp->data_buffer, scp->data_length);
tcp->data_buffer = buf;
tcp->data_limit = scp->data_length;
tcp->data_length = scp->data_length;
}
}
scp = &(screen->selected_cells[which]);
if (scp->data_length == 0) {
TRACE(("XtDisownSelection(%s, @%ld)\n",
TraceAtomName(screen->display, atoms[i]),
(long) screen->selection_time));
XtDisownSelection((Widget) xw,
atoms[i],
screen->selection_time);
} else if (!screen->replyToEmacs && atoms[i] != 0) {
TRACE(("XtOwnSelection(%s, @%ld)\n",
TraceAtomName(screen->display, atoms[i]),
(long) screen->selection_time));
have_selection |=
XtOwnSelection((Widget) xw, atoms[i],
screen->selection_time,
ConvertSelection,
LoseSelection,
SelectionDone);
}
}
TRACE(("... _OwnSelection used length %lu value %s\n",
(unsigned long) scp->data_length,
visibleChars(scp->data_buffer,
(unsigned) scp->data_length)));
}
if (!screen->replyToEmacs)
screen->selection_count = count;
if (!have_selection)
UnHiliteText(xw);
}
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)
{
CLineData *ld = GET_LINEDATA(screen, row);
const 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;
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++) {
unsigned c;
assert(i < (int) ld->lineSize);
c = ld->charData[i];
if (ld->attribs[i] & INVISIBLE)
continue;
#if OPT_WIDE_CHARS
/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
* wide character.
*/
if (c == HIDDEN_CHAR) {
if (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_nrc_mode | screen->utf8_mode) != uFalse) {
size_t off;
for_each_combData(off, ld) {
unsigned ch = ld->combData[off][i];
if (ch == 0)
break;
lp = convertToUTF8(lp, ch);
}
}
});
continue;
} else {
c = ' '; /* should not happen, but just in case... */
}
}
previous = c;
if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
lp = convertToUTF8(lp, (c != 0) ? c : ' ');
if_OPT_WIDE_CHARS(screen, {
size_t off;
for_each_combData(off, ld) {
unsigned ch = ld->combData[off][i];
if (ch == 0)
break;
lp = convertToUTF8(lp, ch);
}
});
} else
#endif
{
if (c == 0) {
c = ' ';
} else if (c < ' ') {
c = DECtoASCII(c);
} else if (c == 0x7f) {
c = 0x5f;
}
*lp++ = CharOf(c);
}
if (c != ' ')
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);
}
/*
* This adds together the bits:
* shift key -> 1
* meta key -> 2
* control key -> 4
*/
static unsigned
KeyState(XtermWidget xw, unsigned x)
{
return ((((x) & (ShiftMask | ControlMask)))
+ (((x) & MetaMask(xw)) ? 2 : 0));
}
/* 32 + following 8-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 (and button 6 and 7)
7 set for buttons 8 to 11
*/
/* Position: 32 - 255. */
static int
BtnCode(XtermWidget xw, XButtonEvent *event, int button)
{
int result = (int) (32 + (KeyState(xw, event->state) << 2));
if (event->type == MotionNotify)
result += 32;
if (button < 0) {
result += 3;
} else {
result += button & 3;
if (button & 4)
result += 64;
if (button & 8)
result += 128;
}
TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
button,
visibleEventType(event->type),
ARG_MODIFIER_NAMES(event->state),
result));
return result;
}
static unsigned
EmitButtonCode(XtermWidget xw,
Char *line,
unsigned count,
XButtonEvent *event,
int button)
{
TScreen *screen = TScreenOf(xw);
int value;
if (okSendMousePos(xw) == X10_MOUSE) {
value = CharOf(' ' + button);
} else {
value = BtnCode(xw, event, button);
}
switch (screen->extend_coords) {
default:
line[count++] = CharOf(value);
break;
case SET_SGR_EXT_MODE_MOUSE:
case SET_PIXEL_POSITION_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;
}
static int
FirstBitN(int bits)
{
int result = -1;
if (bits > 0) {
result = 0;
while (!(bits & 1)) {
bits /= 2;
++result;
}
}
return result;
}
#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
#define EMIT_BUTTON(button) EmitButtonCode(xw, 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++;
/* Ignore buttons that cannot be encoded */
if (screen->send_mouse_pos == X10_MOUSE) {
if (button > 3)
return;
} else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
|| screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
|| screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
if (button > 15) {
return;
}
} else {
if (button > 11) {
return;
}
}
if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
row = event->y - OriginY(screen);
col = event->x - OriginX(screen);
} else {
/* 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:
case SET_PIXEL_POSITION_MOUSE:
line[count++] = '<';
break;
}
/* Add event code to key sequence */
if (okSendMousePos(xw) == X10_MOUSE) {
count = EMIT_BUTTON(button);
} else {
/* Button-Motion events */
switch (event->type) {
case ButtonPress:
screen->mouse_button |= ButtonBit(button);
count = EMIT_BUTTON(button);
break;
case ButtonRelease:
/*
* The (vertical) wheel mouse interface generates release-events
* for buttons 4 and 5.
*
* The X10/X11 xterm protocol maps the release for buttons 1..3 to
* a -1, which will be later mapped into a "0" (some button was
* released), At this point, buttons 1..3 are encoded 0..2 (the
* code 3 is unused).
*
* The SGR (extended) xterm mouse protocol keeps the button number
* and uses a "m" to indicate button release.
*
* The behavior for mice with more buttons is unclear, and may be
* revised -TD
*/
screen->mouse_button &= ~ButtonBit(button);
if (button < 3 || button > 5) {
switch (screen->extend_coords) {
case SET_SGR_EXT_MODE_MOUSE:
case SET_PIXEL_POSITION_MOUSE:
final = 'm';
break;
default:
button = -1;
break;
}
}
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(FirstBitN(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:
case SET_PIXEL_POSITION_MOUSE:
line[count++] = final;
break;
}
/* Transmit key sequence to process running under xterm */
TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
v_write(pty, line, (size_t) count);
}
return;
}
/*
* Check the current send_mouse_pos against allowed mouse-operations, returning
* none if it is disallowed.
*/
XtermMouseModes
okSendMousePos(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
switch ((int) result) {
case MOUSE_OFF:
break;
case X10_MOUSE:
if (!AllowMouseOps(xw, emX10))
result = MOUSE_OFF;
break;
case VT200_MOUSE:
if (!AllowMouseOps(xw, emVT200Click))
result = MOUSE_OFF;
break;
case VT200_HIGHLIGHT_MOUSE:
if (!AllowMouseOps(xw, emVT200Hilite))
result = MOUSE_OFF;
break;
case BTN_EVENT_MOUSE:
if (!AllowMouseOps(xw, emAnyButton))
result = MOUSE_OFF;
break;
case ANY_EVENT_MOUSE:
if (!AllowMouseOps(xw, emAnyEvent))
result = MOUSE_OFF;
break;
case DEC_LOCATOR:
if (!AllowMouseOps(xw, emLocator))
result = MOUSE_OFF;
break;
}
return result;
}
#if OPT_FOCUS_EVENT
/*
* Check the current send_focus_pos against allowed mouse-operations, returning
* none if it is disallowed.
*/
static int
okSendFocusPos(XtermWidget xw)
{
TScreen *screen = TScreenOf(xw);
int result = screen->send_focus_pos;
if (!AllowMouseOps(xw, emFocusEvent)) {
result = False;
}
return result;
}
void
SendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
{
if (okSendFocusPos(xw)) {
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 void
doSelectionFormat(XtermWidget xw,
Widget w,
XEvent *event,
String *params,
Cardinal *num_params,
FormatSelect format_select)
{
TScreen *screen = TScreenOf(xw);
InternalSelect *mydata = &(screen->internal_select);
memset(mydata, 0, sizeof(*mydata));
mydata->format = x_strdup(params[0]);
mydata->format_select = format_select;
screen->selectToBuffer = True;
beginInternalSelect(xw);
xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
if (screen->selectToBuffer)
finishInternalSelect(xw);
}
/* obtain data from the screen, passing the endpoints to caller's parameters */
static char *
getDataFromScreen(XtermWidget xw, XEvent *event, 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;
const Cardinal noClick = 0;
int save_numberOfClicks = screen->numberOfClicks;
SelectUnit saveUnits = screen->selectUnit;
SelectUnit saveMap = screen->selectMap[noClick];
#if OPT_SELECT_REGEX
char *saveExpr = screen->selectExpr[noClick];
#endif
SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
SelectedCells save_selection = *scp;
char *result = 0;
TRACE(("getDataFromScreen %s\n", method));
memset(scp, 0, sizeof(*scp));
screen->numberOfClicks = 1;
lookupSelectUnit(xw, noClick, method);
screen->selectUnit = screen->selectMap[noClick];
memset(start, 0, sizeof(*start));
if (IsBtnEvent(event)) {
XButtonEvent *btn_event = (XButtonEvent *) event;
CELL cell;
screen->firstValidRow = 0;
screen->lastValidRow = screen->max_row;
PointToCELL(screen, btn_event->y, btn_event->x, &cell);
start->row = cell.row;
start->col = cell.col;
finish->row = cell.row;
finish->col = screen->max_col;
} else {
start->row = screen->cur_row;
start->col = screen->cur_col;
finish->row = screen->cur_row;
finish->col = screen->max_col;
}
ComputeSelect(xw, start, finish, False, False);
SaltTextAway(xw,
TargetToSelection(screen, PRIMARY_NAME),
&(screen->startSel), &(screen->endSel));
if (scp->data_limit && scp->data_buffer) {
TRACE(("...getDataFromScreen selection-data %.*s\n",
(int) scp->data_limit,
scp->data_buffer));
result = malloc(scp->data_limit + 1);
if (result) {
memcpy(result, scp->data_buffer, scp->data_limit);
result[scp->data_limit] = 0;
}
free(scp->data_buffer);
scp->data_limit = 0;
}
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->numberOfClicks = save_numberOfClicks;
screen->selectUnit = saveUnits;
screen->selectMap[noClick] = saveMap;
#if OPT_SELECT_REGEX
screen->selectExpr[noClick] = saveExpr;
#endif
screen->selected_cells[0] = save_selection;
TrackText(xw, &save_old_start, &save_old_end);
TRACE(("...getDataFromScreen done\n"));
return result;
}
#if OPT_EXEC_SELECTION
/*
* 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;
format = x_skip_blanks(format);
if (*format != '\0') {
char *blob = x_strdup(format);
int pass;
for (pass = 0; pass < 2; ++pass) {
int used = 0;
int first = 1;
int escaped = 0;
int squoted = 0;
int dquoted = 0;
int n;
int 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) {
free(blob);
break;
}
}
}
}
#if OPT_TRACE
if (result) {
int n;
TRACE(("tokenizeFormat %s\n", format));
for (n = 0; result[n]; ++n) {
TRACE(("argv[%d] = %s\n", n, result[n]));
}
}
#endif
return result;
}
#endif /* OPT_EXEC_SELECTION */
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) {
IAttr 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) {
Pixel 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%lu", delim, fg);
delim = ";";
}
if (attribs & BG_COLOR) {
Pixel 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%lu", delim, bg);
}
#endif
}
}
static char *
formatStrlen(char *target, char *source, int freeit)
{
if (source != 0) {
sprintf(target, "%u", (unsigned) strlen(source));
if (freeit) {
free(source);
}
} else {
strcpy(target, "0");
}
return target;
}
/* 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) {
if (format[n] == '%') {
char *value = 0;
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 'R':
value = formatStrlen(numbers, x_strrtrim(data), 1);
break;
case 'r':
value = x_strrtrim(data);
break;
case 'S':
value = formatStrlen(numbers, data, 0);
break;
case 's':
value = data;
break;
case 'T':
value = formatStrlen(numbers, x_strtrim(data), 1);
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;
}
#if OPT_EXEC_SELECTION
/* execute the command after forking. The main process frees its data */
static void
executeCommand(pid_t pid, char **argv)
{
(void) pid;
if (argv != 0 && argv[0] != 0) {
char *child_cwd = ProcGetCWD(pid);
if (fork() == 0) {
if (child_cwd) {
IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */
}
execvp(argv[0], argv);
exit(EXIT_FAILURE);
}
free(child_cwd);
}
}
static void
freeArgv(char *blob, char **argv)
{
if (blob) {
free(blob);
if (argv) {
int n;
for (n = 0; argv[n]; ++n)
free(argv[n]);
free(argv);
}
}
}
static void
reallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
char **argv;
if ((argv = tokenizeFormat(format)) != 0) {
char *blob = argv[0];
int argc;
for (argc = 0; argv[argc] != 0; ++argc) {
argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
}
executeCommand(TScreenOf(xw)->pid, argv);
freeArgv(blob, argv);
}
}
}
void
HandleExecFormatted(Widget w,
XEvent *event,
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
TRACE_EVENT("HandleExecFormatted", event, params, num_params);
if ((xw = getXtermWidget(w)) != 0 &&
(*num_params > 1)) {
doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
}
}
void
HandleExecSelectable(Widget w,
XEvent *event,
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TRACE_EVENT("HandleExecSelectable", event, params, num_params);
if (*num_params == 2) {
CELL start, finish;
char *data;
char **argv;
data = getDataFromScreen(xw, event, params[1], &start, &finish);
if (data != 0) {
if ((argv = tokenizeFormat(params[0])) != 0) {
char *blob = argv[0];
int argc;
for (argc = 0; argv[argc] != 0; ++argc) {
argv[argc] = expandFormat(xw, argv[argc], data,
&start, &finish);
}
executeCommand(TScreenOf(xw)->pid, argv);
freeArgv(blob, argv);
}
free(data);
}
}
}
}
#endif /* OPT_EXEC_SELECTION */
static void
reallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
char *exps;
if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
unparseputs(xw, exps);
unparse_end(xw);
free(exps);
}
}
}
void
HandleInsertFormatted(Widget w,
XEvent *event,
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
if ((xw = getXtermWidget(w)) != 0 &&
(*num_params > 1)) {
doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
}
}
void
HandleInsertSelectable(Widget w,
XEvent *event,
String *params, /* selections */
Cardinal *num_params)
{
XtermWidget xw;
if ((xw = getXtermWidget(w)) != 0) {
TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
if (*num_params == 2) {
CELL start, finish;
char *data;
char *temp = x_strdup(params[0]);
data = getDataFromScreen(xw, event, params[1], &start, &finish);
if (data != 0) {
char *exps = expandFormat(xw, temp, data, &start, &finish);
if (exps != 0) {
unparseputs(xw, exps);
unparse_end(xw);
free(exps);
}
free(data);
}
free(temp);
}
}
}
#endif /* OPT_SELECTION_OPS */