#include <xterm.h>
#include <stdio.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
#include <X11/Xmu/StdSel.h>
#include <xutf8.h>
#include <data.h>
#include <error.h>
#include <menu.h>
#include <xcharmouse.h>
#include <charclass.h>
#if OPT_WIDE_CHARS
#include <wcwidth.h>
#else
#define CharacterClass(value) \
charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
#endif
#define XTERM_CELL(row,col) getXtermCell(screen, row + screen->topline, col)
#define XTERM_CELL_C1(row,col) getXtermCellComb1(screen, row + screen->topline, col)
#define XTERM_CELL_C2(row,col) getXtermCellComb2(screen, row + screen->topline, col)
#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
#define KeyModifiers (event->xbutton.state & OurModifiers)
#define KeyState(x) (((x) & (ShiftMask|ControlMask)) + (((x) & Mod1Mask) ? 2 : 0))
#define Coordinate(r,c) ((r) * (term->screen.max_col+1) + (c))
#if OPT_DEC_LOCATOR
static ANSI reply;
#endif
static int rawRow, rawCol;
static int startRRow, startRCol, endRRow, endRCol = 0;
static int startSRow, startSCol, endSRow, endSCol = 0;
static int firstValidRow, lastValidRow;
static int startERow, startECol, endERow, endECol;
static int saveStartRRow, saveStartRCol, saveEndRRow, saveEndRCol;
static int saveStartWRow, saveStartWCol;
static int numberOfClicks = 0;
static Time lastButtonUpTime = 0;
#if OPT_READLINE
static Time lastButtonDownTime = 0;
static int ExtendingSelection = 0;
static Time lastButton3UpTime = 0;
static Time lastButton3DoubleDownTime = 0;
static int lastButton3row, lastButton3col;
#endif
typedef int SelectUnit;
#define SELECTCHAR 0
#define SELECTWORD 1
#define SELECTLINE 2
#define NSELECTUNITS 3
static SelectUnit selectUnit;
static int replyToEmacs;
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(int startRow, int startCol, int endRow, int
endCol, Bool extend);
static void EditorButton(XButtonEvent * event);
static void EndExtend(Widget w, XEvent * event, String * params, Cardinal
num_params, Bool use_cursor_loc);
static void ExtendExtend(int row, int col);
static void PointToRowCol(int y, int x, int *r, int *c);
static void ReHiliteText(int frow, int fcol, int trow, int tcol);
static void SaltTextAway(int crow, int ccol, int row, int col, String *
params, Cardinal num_params);
static void SelectSet(Widget w, XEvent * event, String * params, Cardinal num_params);
static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
static void StartSelect(int startrow, int startcol);
static void TrackDown(XButtonEvent * event);
static void _OwnSelection(XtermWidget termw, String * selections, Cardinal count);
static void do_select_end(Widget w, XEvent * event, String * params,
Cardinal * num_params, Bool use_cursor_loc);
Boolean
SendMousePosition(Widget w, XEvent * event)
{
TScreen *screen;
if (!IsXtermWidget(w))
return False;
screen = &((XtermWidget) w)->screen;
if (screen->send_mouse_pos == MOUSE_OFF)
return False;
#if OPT_DEC_LOCATOR
if (screen->send_mouse_pos == DEC_LOCATOR) {
return (SendLocatorPosition(w, event));
}
#endif
if ((screen->send_mouse_pos != BTN_EVENT_MOUSE)
&& (screen->send_mouse_pos != ANY_EVENT_MOUSE)
&& event->type != ButtonPress
&& event->type != ButtonRelease)
return False;
switch (screen->send_mouse_pos) {
case X10_MOUSE:
if (KeyModifiers == 0) {
if (event->type == ButtonPress)
EditorButton((XButtonEvent *) event);
return True;
}
return False;
case VT200_HIGHLIGHT_MOUSE:
if (event->type == ButtonPress &&
KeyModifiers == 0 &&
event->xbutton.button == Button1) {
TrackDown((XButtonEvent *) event);
return True;
}
if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
EditorButton((XButtonEvent *) event);
return True;
}
return False;
case VT200_MOUSE:
case BTN_EVENT_MOUSE:
case ANY_EVENT_MOUSE:
if (KeyModifiers == 0 || KeyModifiers == ControlMask) {
EditorButton((XButtonEvent *) event);
return True;
}
return False;
default:
return False;
}
}
#if OPT_DEC_LOCATOR
#define LocatorCoords( row, col, x, y, oor ) \
if( screen->locator_pixels ) { \
(oor)=FALSE; (row) = (y)+1; (col) = (x)+1; \
\
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; \
\
(row) = ((y) - screen->border) / FontHeight(screen); \
(col) = ((x) - OriginX(screen)) / FontWidth(screen); \
\
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)++; \
}
#define MotionOff( s, t ) { \
(s)->event_mask |= ButtonMotionMask; \
(s)->event_mask &= ~PointerMotionMask; \
XSelectInput(XtDisplay((t)), XtWindow((t)), (s)->event_mask); }
#define MotionOn( s, t ) { \
(s)->event_mask &= ~ButtonMotionMask; \
(s)->event_mask |= PointerMotionMask; \
XSelectInput(XtDisplay((t)), XtWindow((t)), (s)->event_mask); }
Boolean
SendLocatorPosition(Widget w, XEvent * event)
{
TScreen *screen = &((XtermWidget) w)->screen;
int row, col;
Boolean oor;
int button;
int state;
if ((event->type != ButtonPress &&
event->type != ButtonRelease &&
!screen->loc_filter) ||
(KeyModifiers != 0 && KeyModifiers != ControlMask))
return (False);
if ((event->type == ButtonPress &&
!(screen->locator_events & LOC_BTNS_DN)) ||
(event->type == ButtonRelease &&
!(screen->locator_events & LOC_BTNS_UP)))
return (True);
if (event->type == MotionNotify) {
CheckLocatorPosition(w, event);
return (True);
}
button = event->xbutton.button - 1;
LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
reply.a_type = CSI;
if (oor) {
reply.a_nparam = 1;
reply.a_param[0] = 0;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
return (True);
}
reply.a_nparam = 4;
switch (event->type) {
case ButtonPress:
reply.a_param[0] = 2 + (button << 1);
break;
case ButtonRelease:
reply.a_param[0] = 3 + (button << 1);
break;
default:
return (True);
}
state = (event->xbutton.state
& (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
state ^= 1 << button;
state = (state & ~(4 | 1)) | ((state & 1) ? 4 : 0) | ((state & 4) ? 1 : 0);
reply.a_param[1] = state;
reply.a_param[2] = row;
reply.a_param[3] = col;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
if (screen->loc_filter) {
screen->send_mouse_pos = MOUSE_OFF;
screen->loc_filter = FALSE;
screen->locator_events = 0;
MotionOff(screen, term);
}
return (True);
}
#define ButtonState(state, mask) \
{ (state) = ((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8; \
\
(state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0); \
}
void
GetLocatorPosition(XtermWidget w)
{
TScreen *screen = &w->screen;
Window root, child;
int rx, ry, x, y;
unsigned int mask;
int row = 0, col = 0;
Boolean oor = FALSE;
Bool ret = FALSE;
int state;
if (screen->loc_filter) {
screen->send_mouse_pos = MOUSE_OFF;
screen->loc_filter = FALSE;
screen->locator_events = 0;
MotionOff(screen, term);
}
reply.a_type = CSI;
if (screen->send_mouse_pos == DEC_LOCATOR) {
ret = XQueryPointer(screen->display, VWindow(screen), &root,
&child, &rx, &ry, &x, &y, &mask);
if (ret) {
LocatorCoords(row, col, x, y, oor);
}
}
if (ret == FALSE || oor) {
reply.a_nparam = 1;
reply.a_param[0] = 0;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
return;
}
ButtonState(state, mask);
reply.a_nparam = 4;
reply.a_param[0] = 1;
reply.a_param[1] = state;
reply.a_param[2] = row;
reply.a_param[3] = col;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
}
void
InitLocatorFilter(XtermWidget w)
{
TScreen *screen = &w->screen;
Window root, child;
int rx, ry, x, y;
unsigned int mask;
int row = 0, col = 0;
Boolean oor = 0;
Bool ret;
int state;
ret = XQueryPointer(screen->display, VWindow(screen),
&root, &child, &rx, &ry, &x, &y, &mask);
if (ret) {
LocatorCoords(row, col, x, y, oor);
}
if (ret == FALSE || oor) {
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) {
reply.a_type = CSI;
reply.a_nparam = 1;
reply.a_param[0] = 0;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
} else {
screen->loc_filter = TRUE;
MotionOn(screen, term);
}
return;
}
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)) {
ButtonState(state, mask);
reply.a_type = CSI;
reply.a_nparam = 4;
reply.a_param[0] = 10;
reply.a_param[1] = state;
reply.a_param[2] = row;
reply.a_param[3] = col;
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
return;
}
screen->loc_filter = TRUE;
MotionOn(screen, term);
}
void
CheckLocatorPosition(Widget w, XEvent * event)
{
TScreen *screen = &((XtermWidget) w)->screen;
int row, col;
Boolean oor;
int state;
LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor);
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)) {
screen->loc_filter = FALSE;
MotionOff(screen, term);
reply.a_type = CSI;
if (oor) {
reply.a_nparam = 1;
reply.a_param[0] = 0;
} else {
ButtonState(state, event->xbutton.state);
reply.a_nparam = 4;
reply.a_param[0] = 10;
reply.a_param[1] = state;
reply.a_param[2] = row;
reply.a_param[3] = col;
}
reply.a_inters = '&';
reply.a_final = 'w';
unparseseq(&reply, screen->respond);
if (screen->locator_reset) {
MotionOff(screen, term);
screen->send_mouse_pos = MOUSE_OFF;
}
}
}
#endif
#if OPT_READLINE
static int
isClick1_clean(XEvent * event)
{
TScreen *screen = &term->screen;
int delta;
if (!(event->type == ButtonPress || event->type == ButtonRelease)
|| (KeyModifiers & ShiftMask)
|| (screen->send_mouse_pos != MOUSE_OFF)
||ExtendingSelection)
return 0;
if (event->type != ButtonRelease)
return 0;
if (lastButtonDownTime == (Time) 0)
delta = term->screen.multiClickTime + 1;
else if (event->xbutton.time > lastButtonDownTime)
delta = event->xbutton.time - lastButtonDownTime;
else
delta = (((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time;
return delta <= term->screen.multiClickTime;
}
static int
isDoubleClick3(XEvent * event)
{
int delta;
if (event->type != ButtonRelease
|| (KeyModifiers & ShiftMask)
|| event->xbutton.button != Button3) {
lastButton3UpTime = 0;
return 0;
}
if (lastButton3DoubleDownTime == (Time) 0)
delta = term->screen.multiClickTime + 1;
else if (event->xbutton.time > lastButton3DoubleDownTime)
delta = event->xbutton.time - lastButton3DoubleDownTime;
else
delta = (((Time) ~ 0) - lastButton3DoubleDownTime) + event->xbutton.time;
if (delta <= term->screen.multiClickTime) {
int row, col;
PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col);
if (row == lastButton3row && col == lastButton3col) {
lastButton3DoubleDownTime = 0;
return 1;
}
}
lastButton3UpTime = event->xbutton.time;
PointToRowCol(event->xbutton.y, event->xbutton.x,
&lastButton3row, &lastButton3col);
return 0;
}
static int
CheckSecondPress3(XEvent * event)
{
int delta, row, col;
if (event->type != ButtonPress
|| (KeyModifiers & ShiftMask)
|| event->xbutton.button != Button3) {
lastButton3DoubleDownTime = 0;
return 0;
}
if (lastButton3UpTime == (Time) 0)
delta = term->screen.multiClickTime + 1;
else if (event->xbutton.time > lastButton3UpTime)
delta = event->xbutton.time - lastButton3UpTime;
else
delta = (((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time;
if (delta <= term->screen.multiClickTime) {
PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col);
if (row == lastButton3row && col == lastButton3col) {
lastButton3DoubleDownTime = event->xbutton.time;
PointToRowCol(event->xbutton.y, event->xbutton.x,
&lastButton3row, &lastButton3col);
return 1;
}
lastButton3UpTime = 0;
}
lastButton3DoubleDownTime = 0;
return 0;
}
static int
rowOnCurrentLine(int line, int *deltap)
{
TScreen *screen = &term->screen;
int l1, l2;
*deltap = 0;
if (line == screen->cur_row)
return 1;
if (line < screen->cur_row)
l1 = line, l2 = screen->cur_row;
else
l2 = line, l1 = screen->cur_row;
l1--;
while (++l1 < l2)
if (!ScrnTstWrapped(screen, l1))
return 0;
*deltap = line - screen->cur_row;
return 1;
}
static int
eventRow(XEvent * event)
{
TScreen *screen = &term->screen;
return (event->xbutton.y - screen->border) / FontHeight(screen);
}
static int
eventColBetween(XEvent * event)
{
TScreen *screen = &term->screen;
return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
/ FontWidth(screen));
}
static int
ReadLineMovePoint(int col, int ldelta)
{
TScreen *screen = &term->screen;
Char line[6];
int count = 0;
col += ldelta * (screen->max_col + 1) - screen->cur_col;
if (col == 0)
return 0;
if (screen->control_eight_bits) {
line[count++] = CSI;
} else {
line[count++] = ESC;
line[count++] = '[';
}
line[count++] = (col > 0 ? 'C' : 'D');
if (col < 0)
col = -col;
while (col--)
v_write(screen->respond, line, 3);
return 1;
}
static int
ReadLineDelete(int r1, int c1, int r2, int c2)
{
TScreen *screen = &term->screen;
int del;
del = c2 - c1 + (r2 - r1) * (screen->max_col + 1);
if (del <= 0)
return 0;
while (del--)
v_write(screen->respond, "\177", 1);
return 1;
}
#endif
void
DiredButton(Widget w GCC_UNUSED,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED)
{
TScreen *screen = &term->screen;
Char Line[6];
unsigned line, col;
if (event->type == ButtonPress || event->type == ButtonRelease) {
line = (event->xbutton.y - screen->border) / FontHeight(screen);
col = (event->xbutton.x - OriginX(screen)) / FontWidth(screen);
Line[0] = CONTROL('X');
Line[1] = ESC;
Line[2] = 'G';
Line[3] = ' ' + col;
Line[4] = ' ' + line;
v_write(screen->respond, Line, 5);
}
}
#if OPT_READLINE
void
ReadLineButton(Widget w GCC_UNUSED,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED)
{
TScreen *screen = &term->screen;
Char Line[6];
int line, col, ldelta = 0;
if (!(event->type == ButtonPress || event->type == ButtonRelease)
|| (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection)
goto finish;
if (event->type == ButtonRelease) {
int delta;
if (lastButtonDownTime == (Time) 0)
delta = screen->multiClickTime + 1;
else if (event->xbutton.time > lastButtonDownTime)
delta = event->xbutton.time - lastButtonDownTime;
else
delta = (((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time;
if (delta > screen->multiClickTime)
goto finish;
}
line = (event->xbutton.y - screen->border) / FontHeight(screen);
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)
if (!ScrnTstWrapped(screen, l1))
goto finish;
ldelta = line - screen->cur_row;
}
col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
/ FontWidth(screen) - screen->cur_col + ldelta * (screen->max_col + 1);
if (col == 0)
goto finish;
Line[0] = ESC;
Line[1] = 'O';
Line[2] = (col > 0 ? 'C' : 'D');
if (col < 0)
col = -col;
while (col--)
v_write(screen->respond, Line, 3);
finish:
if (event->type == ButtonRelease)
do_select_end(w, event, params, num_params, False);
}
#endif
void
ViButton(Widget w GCC_UNUSED,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED)
{
TScreen *screen = &term->screen;
int pty = screen->respond;
Char Line[6];
int line;
if (event->type == ButtonPress || event->type == ButtonRelease) {
line = screen->cur_row -
((event->xbutton.y - screen->border) / FontHeight(screen));
if (line != 0) {
Line[0] = ESC;
v_write(pty, Line, 1);
if (line < 0) {
line = -line;
Line[0] = CONTROL('n');
} else {
Line[0] = CONTROL('p');
}
while (--line >= 0)
v_write(pty, Line, 1);
}
}
}
void
HandleSelectExtend(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED)
{
TScreen *screen;
int row, col;
if (!IsXtermWidget(w))
return;
screen = &((XtermWidget) w)->screen;
screen->selection_time = event->xmotion.time;
switch (eventMode) {
case LEFTEXTENSION:
case RIGHTEXTENSION:
PointToRowCol(event->xmotion.y, event->xmotion.x,
&row, &col);
ExtendExtend(row, col);
break;
case NORMAL:
if (screen->send_mouse_pos == BTN_EVENT_MOUSE
|| screen->send_mouse_pos == ANY_EVENT_MOUSE)
SendMousePosition(w, event);
break;
}
}
static void
do_select_end(Widget w,
XEvent * event,
String * params,
Cardinal * num_params,
Bool use_cursor_loc)
{
#if OPT_READLINE
int ldelta1, ldelta2;
TScreen *screen = &term->screen;
#endif
if (!IsXtermWidget(w))
return;
((XtermWidget) w)->screen.selection_time = event->xbutton.time;
switch (eventMode) {
case NORMAL:
(void) SendMousePosition(w, event);
break;
case LEFTEXTENSION:
case RIGHTEXTENSION:
EndExtend(w, event, params, *num_params, use_cursor_loc);
#if OPT_READLINE
if (isClick1_clean(event)
&& SCREEN_FLAG(screen, click1_moves)
&& rowOnCurrentLine(eventRow(event), &ldelta1)) {
ReadLineMovePoint(eventColBetween(event), ldelta1);
}
if (isDoubleClick3(event)
&& SCREEN_FLAG(screen, dclick3_deletes)
&& rowOnCurrentLine(startSRow, &ldelta1)
&& rowOnCurrentLine(endSRow, &ldelta2)) {
ReadLineMovePoint(endSCol, ldelta2);
ReadLineDelete(startSRow, startSCol, endSRow, endSCol);
}
#endif
break;
}
}
void
HandleSelectEnd(Widget w,
XEvent * event,
String * params,
Cardinal * num_params)
{
do_select_end(w, event, params, num_params, False);
}
void
HandleKeyboardSelectEnd(Widget w,
XEvent * event,
String * params,
Cardinal * num_params)
{
do_select_end(w, event, params, num_params, True);
}
struct _SelectionList {
String *params;
Cardinal count;
Atom *targets;
Time time;
};
#if OPT_WIDE_CHARS
static Char *
UTF8toLatin1(Char * s, int len, unsigned long *result)
{
static Char *buffer;
static size_t used;
Char *p = s;
Char *q;
if (used == 0) {
buffer = (Char *) XtMalloc(used = len);
} else if (len > (int) used) {
buffer = (Char *) XtRealloc((char *) buffer, used = len);
}
q = buffer;
while (p < s + len) {
if ((*p & 0x80) == 0) {
*q++ = *p++;
} else if ((*p & 0x7C) == 0x40 && p < s + len - 1) {
*q++ = ((*p & 0x03) << 6) | (p[1] & 0x3F);
p += 2;
} else if ((*p & 0x60) == 0x40) {
*q++ = '#';
p += 2;
} else if ((*p & 0x50) == 0x40) {
*q++ = '#';
p += 3;
} else {
*q++ = '#';
p++;
}
}
*result = q - buffer;
return buffer;
}
#endif
static Atom *
_SelectionTargets(Widget w)
{
static Atom *eightBitSelectionTargets = NULL;
TScreen *screen;
int n;
if (!IsXtermWidget(w))
return NULL;
screen = &((XtermWidget) w)->screen;
#if OPT_WIDE_CHARS
if (screen->wide_chars) {
static Atom *utf8SelectionTargets = NULL;
if (utf8SelectionTargets == NULL) {
utf8SelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom));
if (utf8SelectionTargets == NULL) {
TRACE(("Couldn't allocate utf8SelectionTargets\n"));
return NULL;
}
n = 0;
utf8SelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w));
#ifdef X_HAVE_UTF8_STRING
if (screen->i18nSelections) {
utf8SelectionTargets[n++] = XA_TEXT(XtDisplay(w));
utf8SelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
}
#endif
utf8SelectionTargets[n++] = XA_STRING;
utf8SelectionTargets[n] = None;
}
return utf8SelectionTargets;
}
#endif
if (eightBitSelectionTargets == NULL) {
eightBitSelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom));
if (eightBitSelectionTargets == NULL) {
TRACE(("Couldn't allocate eightBitSelectionTargets\n"));
return NULL;
}
n = 0;
#ifdef X_HAVE_UTF8_STRING
eightBitSelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w));
#endif
if (screen->i18nSelections) {
eightBitSelectionTargets[n++] = XA_TEXT(XtDisplay(w));
eightBitSelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
}
eightBitSelectionTargets[n++] = XA_STRING;
eightBitSelectionTargets[n] = None;
}
return eightBitSelectionTargets;
}
static void
_GetSelection(Widget w,
Time ev_time,
String * params,
Cardinal num_params,
Atom * targets)
{
Atom selection;
int cutbuffer;
Atom target;
if (!IsXtermWidget(w))
return;
XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
switch (selection) {
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;
}
TRACE(("Cutbuffer: %d, target: %lu\n", cutbuffer,
targets ? (unsigned long) targets[0] : 0));
if (cutbuffer >= 0) {
int inbytes;
unsigned long nbytes;
int fmt8 = 8;
Atom type = XA_STRING;
char *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)
_GetSelection(w, ev_time, params + 1, num_params - 1, NULL);
return;
} else {
struct _SelectionList *list;
if (targets == NULL || targets[0] == None) {
targets = _SelectionTargets(w);
}
if (targets != 0) {
target = targets[0];
if (targets[1] == None) {
params++;
num_params--;
targets = _SelectionTargets(w);
} else {
targets = &(targets[1]);
}
if (num_params) {
list = XtNew(struct _SelectionList);
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, int len)
{
Char *cp;
char *name;
name = XGetAtomName(dpy, type);
Trace("Getting %s (%ld)\n", XGetAtomName(dpy, type), (long int) type);
for (cp = line; cp < line + len; cp++)
Trace("%c\n", *cp);
}
#else
#define GettingSelection(dpy,type,line,len)
#endif
#ifdef VMS
# define tty_vwrite(pty,lag,l) tt_write(lag,l)
#else
# define tty_vwrite(pty,lag,l) v_write(pty,lag,l)
#endif
static void
_qWriteSelectionData(TScreen * screen, Char * lag, int length)
{
#if OPT_READLINE
if (SCREEN_FLAG(screen, paste_quotes)) {
while (length--) {
tty_vwrite(screen->respond, "\026", 1);
tty_vwrite(screen->respond, lag++, 1);
}
} else
#endif
tty_vwrite(screen->respond, lag, length);
}
static void
_WriteSelectionData(TScreen * screen, Char * line, int length)
{
Char *lag, *cp, *end;
#ifdef VMS
tt_pasting = TRUE;
#endif
end = &line[length];
lag = line;
if (!SCREEN_FLAG(screen, paste_literal_nl)) {
for (cp = line; cp != end; cp++) {
if (*cp == '\n') {
*cp = '\r';
_qWriteSelectionData(screen, lag, cp - lag + 1);
lag = cp + 1;
}
}
}
if (lag != end) {
_qWriteSelectionData(screen, lag, end - lag);
}
#ifdef VMS
tt_pasting = FALSE;
tt_start_read();
#endif
}
#if OPT_READLINE
static void
_WriteKey(TScreen * screen, Char * in)
{
char line[16];
int count = 0, length = strlen(in);
if (screen->control_eight_bits) {
line[count++] = CSI;
} else {
line[count++] = ESC;
line[count++] = '[';
}
while (length--)
line[count++] = *in++;
line[count++] = '~';
tty_vwrite(screen->respond, line, count);
}
#endif
static void
SelectionReceived(Widget w,
XtPointer client_data,
Atom * selection GCC_UNUSED,
Atom * type,
XtPointer value,
unsigned long *length,
int *format GCC_UNUSED)
{
char **text_list = NULL;
int text_list_count;
XTextProperty text_prop;
TScreen *screen;
Display *dpy;
#if OPT_TRACE && OPT_WIDE_CHARS
Char *line = (Char *) value;
#endif
if (!IsXtermWidget(w))
return;
screen = &((XtermWidget) w)->screen;
dpy = XtDisplay(w);
if (*type == 0
|| *length == 0
|| value == NULL)
goto fail;
text_prop.value = (unsigned char *) value;
text_prop.encoding = *type;
text_prop.format = *format;
text_prop.nitems = *length;
#if OPT_WIDE_CHARS
if (screen->wide_chars) {
if (*type == XA_UTF8_STRING(XtDisplay(w)) ||
*type == XA_STRING ||
*type == XA_COMPOUND_TEXT(XtDisplay(w))) {
GettingSelection(dpy, *type, line, *length);
if (Xutf8TextPropertyToTextList(dpy, &text_prop,
&text_list,
&text_list_count) < 0) {
TRACE(("Conversion failed\n"));
text_list = NULL;
}
}
} else
#endif
{
if (*type == XA_UTF8_STRING(XtDisplay(w)) ||
*type == XA_STRING ||
*type == XA_COMPOUND_TEXT(XtDisplay(w))) {
Status rc;
GettingSelection(dpy, *type, line, *length);
if (*type == XA_STRING && 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_READLINE
if (SCREEN_FLAG(screen, paste_brackets))
_WriteKey(screen, "200");
#endif
for (i = 0; i < text_list_count; i++) {
int len = strlen(text_list[i]);
_WriteSelectionData(screen, (Char *) text_list[i], len);
}
#if OPT_READLINE
if (SCREEN_FLAG(screen, paste_brackets))
_WriteKey(screen, "201");
#endif
XFreeStringList(text_list);
} else
goto fail;
XtFree((char *) client_data);
XtFree((char *) value);
return;
fail:
if (client_data != 0) {
struct _SelectionList *list = (struct _SelectionList *) client_data;
_GetSelection(w, list->time,
list->params, list->count, list->targets);
XtFree((char *) client_data);
}
return;
}
void
HandleInsertSelection(Widget w,
XEvent * event,
String * params,
Cardinal * num_params)
{
#if OPT_READLINE
int ldelta;
TScreen *screen = &((XtermWidget) w)->screen;
#endif
if (SendMousePosition(w, event))
return;
#if OPT_READLINE
if ((event->type == ButtonPress || event->type == ButtonRelease)
&& !(KeyModifiers & ShiftMask)
&& (screen->send_mouse_pos == MOUSE_OFF)
&& SCREEN_FLAG(screen, paste_moves)
&& rowOnCurrentLine(eventRow(event), &ldelta))
ReadLineMovePoint(eventColBetween(event), ldelta);
#endif
_GetSelection(w, event->xbutton.time, params, *num_params, NULL);
}
static SelectUnit
EvalSelectUnit(Time buttonDownTime, SelectUnit defaultUnit)
{
int delta;
if (lastButtonUpTime == (Time) 0)
delta = term->screen.multiClickTime + 1;
else if (buttonDownTime > lastButtonUpTime)
delta = buttonDownTime - lastButtonUpTime;
else
delta = (((Time) ~ 0) - lastButtonUpTime) + buttonDownTime;
if (delta > term->screen.multiClickTime) {
numberOfClicks = 1;
return defaultUnit;
} else {
++numberOfClicks;
return ((selectUnit + 1) % NSELECTUNITS);
}
}
static void
do_select_start(Widget w,
XEvent * event,
int startrow,
int startcol)
{
if (SendMousePosition(w, event))
return;
selectUnit = EvalSelectUnit(event->xbutton.time, SELECTCHAR);
replyToEmacs = FALSE;
#if OPT_READLINE
lastButtonDownTime = event->xbutton.time;
#endif
StartSelect(startrow, startcol);
}
void
HandleSelectStart(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED)
{
TScreen *screen;
int startrow, startcol;
if (!IsXtermWidget(w))
return;
screen = &((XtermWidget) w)->screen;
firstValidRow = 0;
lastValidRow = screen->max_row;
PointToRowCol(event->xbutton.y, event->xbutton.x, &startrow, &startcol);
#if OPT_READLINE
ExtendingSelection = 0;
#endif
do_select_start(w, event, startrow, startcol);
}
void
HandleKeyboardSelectStart(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED)
{
TScreen *screen;
if (!IsXtermWidget(w))
return;
screen = &((XtermWidget) w)->screen;
do_select_start(w, event, screen->cursor_row, screen->cursor_col);
}
static void
TrackDown(XButtonEvent * event)
{
int startrow, startcol;
selectUnit = EvalSelectUnit(event->time, SELECTCHAR);
if (numberOfClicks > 1) {
PointToRowCol(event->y, event->x, &startrow, &startcol);
replyToEmacs = TRUE;
StartSelect(startrow, startcol);
} else {
waitingForTrackInfo = 1;
EditorButton((XButtonEvent *) event);
}
}
#define boundsCheck(x) if (x < 0) \
x = 0; \
else if (x >= screen->max_row) \
x = screen->max_row;
void
TrackMouse(int func, int startrow, int startcol, int firstrow, int lastrow)
{
TScreen *screen = &term->screen;
if (!waitingForTrackInfo) {
return;
}
waitingForTrackInfo = 0;
if (func == 0)
return;
boundsCheck(startrow)
boundsCheck(firstrow)
boundsCheck(lastrow)
firstValidRow = firstrow;
lastValidRow = lastrow;
replyToEmacs = TRUE;
StartSelect(startrow, startcol);
}
static void
StartSelect(int startrow, int startcol)
{
TScreen *screen = &term->screen;
TRACE(("StartSelect row=%d, col=%d\n", startrow, startcol));
if (screen->cursor_state)
HideCursor();
if (numberOfClicks == 1) {
rawRow = startrow;
rawCol = startcol;
}
saveStartRRow = startERow = rawRow;
saveStartRCol = startECol = rawCol;
saveEndRRow = endERow = rawRow;
saveEndRCol = endECol = rawCol;
if (Coordinate(startrow, startcol) < Coordinate(rawRow, rawCol)) {
eventMode = LEFTEXTENSION;
startERow = startrow;
startECol = startcol;
} else {
eventMode = RIGHTEXTENSION;
endERow = startrow;
endECol = startcol;
}
ComputeSelect(startERow, startECol, endERow, endECol, False);
}
static void
EndExtend(Widget w,
XEvent * event,
String * params,
Cardinal num_params,
Bool use_cursor_loc)
{
int row, col, count;
TScreen *screen = &term->screen;
Char line[9];
if (use_cursor_loc) {
row = screen->cursor_row;
col = screen->cursor_col;
} else {
PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col);
}
ExtendExtend(row, col);
lastButtonUpTime = event->xbutton.time;
if (startSRow != endSRow || startSCol != endSCol) {
if (replyToEmacs) {
count = 0;
if (screen->control_eight_bits) {
line[count++] = CSI;
} else {
line[count++] = ESC;
line[count++] = '[';
}
if (rawRow == startSRow && rawCol == startSCol
&& row == endSRow && col == endSCol) {
line[count++] = 't';
line[count++] = ' ' + endSCol + 1;
line[count++] = ' ' + endSRow + 1;
} else {
line[count++] = 'T';
line[count++] = ' ' + startSCol + 1;
line[count++] = ' ' + startSRow + 1;
line[count++] = ' ' + endSCol + 1;
line[count++] = ' ' + endSRow + 1;
line[count++] = ' ' + col + 1;
line[count++] = ' ' + row + 1;
}
v_write(screen->respond, line, count);
TrackText(0, 0, 0, 0);
}
}
SelectSet(w, event, params, num_params);
eventMode = NORMAL;
}
void
HandleSelectSet(Widget w,
XEvent * event,
String * params,
Cardinal * num_params)
{
SelectSet(w, event, params, *num_params);
}
static void
SelectSet(Widget w GCC_UNUSED,
XEvent * event GCC_UNUSED,
String * params,
Cardinal num_params)
{
if (startSRow != endSRow || startSCol != endSCol) {
SaltTextAway(startSRow, startSCol, endSRow, endSCol,
params, num_params);
} else
DisownSelection(term);
}
#define Abs(x) ((x) < 0 ? -(x) : (x))
static void
do_start_extend(Widget w,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * num_params GCC_UNUSED,
Bool use_cursor_loc)
{
TScreen *screen;
int row, col, coord;
if (!IsXtermWidget(w))
return;
screen = &((XtermWidget) w)->screen;
if (SendMousePosition(w, event))
return;
firstValidRow = 0;
lastValidRow = screen->max_row;
#if OPT_READLINE
if ((KeyModifiers & ShiftMask)
|| event->xbutton.button != Button3
|| !(SCREEN_FLAG(screen, dclick3_deletes)))
#endif
selectUnit = EvalSelectUnit(event->xbutton.time, selectUnit);
replyToEmacs = FALSE;
#if OPT_READLINE
CheckSecondPress3(event);
#endif
if (numberOfClicks == 1
|| (SCREEN_FLAG(screen, dclick3_deletes)
&&!(KeyModifiers & ShiftMask))) {
saveStartRRow = startERow = startRRow;
saveStartRCol = startECol = startRCol;
saveEndRRow = endERow = endRRow;
saveEndRCol = endECol = endRCol;
} else {
startERow = startRRow = saveStartRRow;
startECol = startRCol = saveStartRCol;
endERow = endRRow = saveEndRRow;
endECol = endRCol = saveEndRCol;
}
if (use_cursor_loc) {
row = screen->cursor_row;
col = screen->cursor_col;
} else {
PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col);
}
coord = Coordinate(row, col);
if (Abs(coord - Coordinate(startSRow, startSCol))
< Abs(coord - Coordinate(endSRow, endSCol))
|| coord < Coordinate(startSRow, startSCol)) {
eventMode = LEFTEXTENSION;
startERow = row;
startECol = col;
} else {
eventMode = RIGHTEXTENSION;
endERow = row;
endECol = col;
}
ComputeSelect(startERow, startECol, endERow, endECol, True);
#if OPT_READLINE
if (startSRow != endSRow || startSCol != endSCol)
ExtendingSelection = 1;
#endif
}
static void
ExtendExtend(int row, int col)
{
int coord = Coordinate(row, col);
TRACE(("ExtendExtend row=%d, col=%d\n", row, col));
if (eventMode == LEFTEXTENSION
&& (coord + (selectUnit != SELECTCHAR)) > Coordinate(endSRow, endSCol)) {
eventMode = RIGHTEXTENSION;
startERow = saveStartRRow;
startECol = saveStartRCol;
} else if (eventMode == RIGHTEXTENSION
&& coord < Coordinate(startSRow, startSCol)) {
eventMode = LEFTEXTENSION;
endERow = saveEndRRow;
endECol = saveEndRCol;
}
if (eventMode == LEFTEXTENSION) {
startERow = row;
startECol = col;
} else {
endERow = row;
endECol = col;
}
ComputeSelect(startERow, startECol, endERow, endECol, False);
#if OPT_READLINE
if (startSRow != endSRow || startSCol != endSCol)
ExtendingSelection = 1;
#endif
}
void
HandleStartExtend(Widget w,
XEvent * event,
String * params,
Cardinal * num_params)
{
do_start_extend(w, event, params, num_params, False);
}
void
HandleKeyboardStartExtend(Widget w,
XEvent * event,
String * params,
Cardinal * num_params)
{
do_start_extend(w, event, params, num_params, True);
}
void
ScrollSelection(TScreen * screen, int amount)
{
int minrow = -screen->savedlines - screen->topline;
int maxrow = screen->max_row - screen->topline;
int maxcol = screen->max_col;
#define scroll_update_one(row, col) \
row += amount; \
if (row < minrow) { \
row = minrow; \
col = 0; \
} \
if (row > maxrow) { \
row = maxrow; \
col = maxcol; \
}
scroll_update_one(startRRow, startRCol);
scroll_update_one(endRRow, endRCol);
scroll_update_one(startSRow, startSCol);
scroll_update_one(endSRow, endSCol);
scroll_update_one(rawRow, rawCol);
scroll_update_one(screen->startHRow, screen->startHCol);
scroll_update_one(screen->endHRow, screen->endHCol);
screen->startHCoord = Coordinate(screen->startHRow, screen->startHCol);
screen->endHCoord = Coordinate(screen->endHRow, screen->endHCol);
}
void
ResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols)
{
rows--;
cols--;
if (startRRow > rows)
startRRow = rows;
if (startSRow > rows)
startSRow = rows;
if (endRRow > rows)
endRRow = rows;
if (endSRow > rows)
endSRow = rows;
if (rawRow > rows)
rawRow = rows;
if (startRCol > cols)
startRCol = cols;
if (startSCol > cols)
startSCol = cols;
if (endRCol > cols)
endRCol = cols;
if (endSCol > cols)
endSCol = cols;
if (rawCol > cols)
rawCol = cols;
}
#if OPT_WIDE_CHARS
int
iswide(int i)
{
return (i == HIDDEN_CHAR) || (my_wcwidth(i) == 2);
}
#endif
static void
PointToRowCol(int y,
int x,
int *r,
int *c)
{
TScreen *screen = &term->screen;
int row, col;
row = (y - screen->border) / FontHeight(screen);
if (row < firstValidRow)
row = firstValidRow;
else if (row > lastValidRow)
row = lastValidRow;
col = (x - OriginX(screen)) / FontWidth(screen);
if (col < 0)
col = 0;
else if (col > screen->max_col + 1) {
col = screen->max_col + 1;
}
#if OPT_WIDE_CHARS
if (col > 0
&& iswide(XTERM_CELL(row, col - 1))
&& (XTERM_CELL(row, col) == HIDDEN_CHAR)) {
col -= 1;
}
#endif
*r = row;
*c = col;
}
static int
LastTextCol(int row)
{
TScreen *screen = &term->screen;
int i;
Char *ch;
if ((row += screen->topline) + screen->savedlines >= 0) {
for (i = screen->max_col,
ch = SCRN_BUF_ATTRS(screen, row) + i;
i >= 0 && !(*ch & CHARDRAWN);
ch--, i--) ;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, row)[0])) {
i *= 2;
}
#endif
} else {
i = -1;
}
return (i);
}
#if !OPT_WIDE_CHARS
static int charClass[256] =
{
32, 1, 1, 1, 1, 1, 1, 1,
1, 32, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 58, 59, 60, 61, 62, 63,
64, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 91, 92, 93, 94, 48,
96, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 123, 124, 125, 126, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183,
184, 185, 186, 187, 188, 189, 190, 191,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 215,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 247,
48, 48, 48, 48, 48, 48, 48, 48};
int
SetCharacterClassRange(int low,
int high,
int value)
{
if (low < 0 || high > 255 || high < low)
return (-1);
for (; low <= high; low++)
charClass[low] = value;
return (0);
}
#endif
#if OPT_WIDE_CHARS
static int
class_of(TScreen * screen, int row, int col)
{
unsigned value;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, row + screen->topline)[0])) {
col /= 2;
}
#endif
value = XTERM_CELL(row, col);
if_OPT_WIDE_CHARS(screen, {
return CharacterClass(value);
});
return CharacterClass(value);
}
#define ClassSelects(screen, row, col, cclass) \
(class_of(screen, row, col) == cclass \
|| XTERM_CELL(row, col) == HIDDEN_CHAR)
#else
#define class_of(screen,row,col) charClass[XTERM_CELL(row, col)]
#define ClassSelects(screen, row, col, cclass) \
(class_of(screen,row, col) == cclass)
#endif
static void
ComputeSelect(int startRow,
int startCol,
int endRow,
int endCol,
Bool extend)
{
TScreen *screen = &term->screen;
int length;
int cclass;
TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
startRow, startCol, endRow, endCol, extend ? "" : "no"));
#if OPT_WIDE_CHARS
if (startCol > 1
&& iswide(XTERM_CELL(startRow, startCol - 1))
&& XTERM_CELL(startRow, startCol - 0) == HIDDEN_CHAR) {
fprintf(stderr, "Adjusting start. Changing downwards from %i.\n", startCol);
startCol -= 1;
if (endCol == (startCol + 1))
endCol--;
}
if (iswide(XTERM_CELL(endRow, endCol - 1))
&& XTERM_CELL(endRow, endCol) == HIDDEN_CHAR) {
endCol += 1;
}
#endif
if (Coordinate(startRow, startCol) <= Coordinate(endRow, endCol)) {
startSRow = startRRow = startRow;
startSCol = startRCol = startCol;
endSRow = endRRow = endRow;
endSCol = endRCol = endCol;
} else {
startSRow = startRRow = endRow;
startSCol = startRCol = endCol;
endSRow = endRRow = startRow;
endSCol = endRCol = startCol;
}
switch (selectUnit) {
case SELECTCHAR:
if (startSCol > (LastTextCol(startSRow) + 1)) {
startSCol = 0;
startSRow++;
}
if (endSCol > (LastTextCol(endSRow) + 1)) {
endSCol = 0;
endSRow++;
}
break;
case SELECTWORD:
if (startSCol > (LastTextCol(startSRow) + 1)) {
startSCol = 0;
startSRow++;
} else {
cclass = class_of(screen, startSRow, startSCol);
do {
--startSCol;
if (startSCol < 0
&& ScrnTstWrapped(screen, startSRow - 1)) {
--startSRow;
startSCol = LastTextCol(startSRow);
}
} while (startSCol >= 0
&& ClassSelects(screen, startSRow, startSCol, cclass));
++startSCol;
}
#if OPT_WIDE_CHARS
if (startSCol && XTERM_CELL(startSRow, startSCol) == HIDDEN_CHAR)
startSCol++;
#endif
if (endSCol > (LastTextCol(endSRow) + 1)) {
endSCol = 0;
endSRow++;
} else {
length = LastTextCol(endSRow);
cclass = class_of(screen, endSRow, endSCol);
do {
++endSCol;
if (endSCol > length
&& ScrnTstWrapped(screen, endSRow)) {
endSCol = 0;
++endSRow;
length = LastTextCol(endSRow);
}
} while (endSCol <= length
&& ClassSelects(screen, endSRow, endSCol, cclass));
if (endSCol > length + 1) {
endSCol = 0;
++endSRow;
}
}
#if OPT_WIDE_CHARS
if (endSCol && XTERM_CELL(endSRow, endSCol) == HIDDEN_CHAR)
endSCol++;
#endif
saveStartWRow = startSRow;
saveStartWCol = startSCol;
break;
case SELECTLINE:
while (ScrnTstWrapped(screen, endSRow)) {
++endSRow;
}
if (term->screen.cutToBeginningOfLine
|| startSRow < saveStartWRow) {
startSCol = 0;
while (ScrnTstWrapped(screen, startSRow - 1)) {
--startSRow;
}
} else if (!extend) {
if ((startRow < saveStartWRow)
|| (startRow == saveStartWRow
&& startCol < saveStartWCol)) {
startSCol = 0;
while (ScrnTstWrapped(screen, startSRow - 1)) {
--startSRow;
}
} else {
startSRow = saveStartWRow;
startSCol = saveStartWCol;
}
}
if (term->screen.cutNewline) {
endSCol = 0;
++endSRow;
} else {
endSCol = LastTextCol(endSRow) + 1;
}
break;
}
ScrollSelection(screen, 0);
TrackText(startSRow, startSCol, endSRow, endSCol);
return;
}
void
TrackText(int frow,
int fcol,
int trow,
int tcol)
{
int from, to;
TScreen *screen = &term->screen;
int old_startrow, old_startcol, old_endrow, old_endcol;
TRACE(("TrackText(frow=%d, fcol=%d, trow=%d, tcol=%d)\n",
frow, fcol, trow, tcol));
old_startrow = screen->startHRow;
old_startcol = screen->startHCol;
old_endrow = screen->endHRow;
old_endcol = screen->endHCol;
if (frow == old_startrow && fcol == old_startcol &&
trow == old_endrow && tcol == old_endcol)
return;
screen->startHRow = frow;
screen->startHCol = fcol;
screen->endHRow = trow;
screen->endHCol = tcol;
from = Coordinate(frow, fcol);
to = Coordinate(trow, tcol);
if (to <= screen->startHCoord || from > screen->endHCoord) {
ReHiliteText(old_startrow, old_startcol, old_endrow, old_endcol);
ReHiliteText(frow, fcol, trow, tcol);
} else {
if (from < screen->startHCoord) {
ReHiliteText(frow, fcol, old_startrow, old_startcol);
} else if (from > screen->startHCoord) {
ReHiliteText(old_startrow, old_startcol, frow, fcol);
}
if (to > screen->endHCoord) {
ReHiliteText(old_endrow, old_endcol, trow, tcol);
} else if (to < screen->endHCoord) {
ReHiliteText(trow, tcol, old_endrow, old_endcol);
}
}
screen->startHCoord = from;
screen->endHCoord = to;
}
static void
ReHiliteText(int frow,
int fcol,
int trow,
int tcol)
{
TScreen *screen = &term->screen;
int i;
if (frow < 0)
frow = fcol = 0;
else if (frow > screen->max_row)
return;
if (trow < 0)
return;
else if (trow > screen->max_row) {
trow = screen->max_row;
tcol = screen->max_col + 1;
}
if (frow == trow && fcol == tcol)
return;
if (frow != trow) {
if ((i = screen->max_col - fcol + 1) > 0) {
ScrnRefresh(screen, frow, fcol, 1, i, True);
}
if ((i = trow - frow - 1) > 0) {
ScrnRefresh(screen, frow + 1, 0, i, screen->max_col + 1, True);
}
if (tcol > 0 && trow <= screen->max_row) {
ScrnRefresh(screen, trow, 0, 1, tcol, True);
}
} else {
ScrnRefresh(screen, frow, fcol, 1, tcol - fcol, True);
}
}
static void
SaltTextAway(int crow, int ccol, int row, int col,
String * params,
Cardinal num_params)
{
TScreen *screen = &term->screen;
int i, j = 0;
int eol;
Char *line;
Char *lp;
if (crow == row && ccol > col) {
int tmp = ccol;
ccol = col;
col = tmp;
}
--col;
if (row == crow) {
j = Length(screen, crow, ccol, col);
} else {
j += Length(screen, crow, ccol, screen->max_col) + 1;
for (i = crow + 1; i < row; i++)
j += Length(screen, i, 0, screen->max_col) + 1;
if (col >= 0)
j += Length(screen, row, 0, col);
}
if_OPT_WIDE_CHARS(screen, {
j *= 4;
});
if (screen->selection_size <= j) {
if ((line = (Char *) malloc((unsigned) j + 1)) == 0)
SysError(ERROR_BMALLOC2);
XtFree((char *) screen->selection_data);
screen->selection_data = line;
screen->selection_size = j + 1;
} else {
line = screen->selection_data;
}
if ((line == 0)
|| (j < 0))
return;
line[j] = '\0';
lp = line;
if (row == crow) {
lp = SaveText(screen, row, ccol, col, lp, &eol);
} else {
lp = SaveText(screen, crow, ccol, screen->max_col, lp, &eol);
if (eol)
*lp++ = '\n';
for (i = crow + 1; i < row; i++) {
lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
if (eol)
*lp++ = '\n';
}
if (col >= 0)
lp = SaveText(screen, row, 0, col, lp, &eol);
}
*lp = '\0';
TRACE(("Salted TEXT:%.*s\n", lp - line, line));
screen->selection_length = (lp - line);
_OwnSelection(term, params, num_params);
}
static Boolean
_ConvertSelectionHelper(Widget w,
Atom * type, XtPointer * value,
unsigned long *length, int *format,
int (*conversion_function) (Display *,
char **, int,
XICCEncodingStyle,
XTextProperty *),
XICCEncodingStyle conversion_style)
{
Display *d = XtDisplay(w);
TScreen *screen;
XTextProperty textprop;
if (!IsXtermWidget(w))
return False;
screen = &((XtermWidget) w)->screen;
if (conversion_function(d, (char **) &screen->selection_data, 1,
conversion_style,
&textprop) < Success)
return False;
*value = (XtPointer) textprop.value;
*length = textprop.nitems;
*type = textprop.encoding;
*format = textprop.format;
return True;
}
static Boolean
ConvertSelection(Widget w,
Atom * selection,
Atom * target,
Atom * type,
XtPointer * value,
unsigned long *length,
int *format)
{
Display *d = XtDisplay(w);
TScreen *screen;
Boolean result = False;
if (!IsXtermWidget(w))
return False;
screen = &((XtermWidget) w)->screen;
if (screen->selection_data == NULL)
return False;
if (*target == XA_TARGETS(d)) {
Atom *targetP;
Atom *std_targets;
XPointer std_return = 0;
unsigned long std_length;
if (XmuConvertStandardSelection(w, screen->selection_time, selection,
target, type, &std_return,
&std_length, format)) {
std_targets = (Atom *) (std_return);
*length = std_length + 6;
targetP = (Atom *) XtMalloc(sizeof(Atom) * (*length));
*value = (XtPointer) targetP;
*targetP++ = XA_STRING;
*targetP++ = XA_TEXT(d);
#ifdef X_HAVE_UTF8_STRING
*targetP++ = XA_COMPOUND_TEXT(d);
*targetP++ = XA_UTF8_STRING(d);
#else
*targetP = XA_COMPOUND_TEXT(d);
if_OPT_WIDE_CHARS(screen, {
*targetP = XA_UTF8_STRING(d);
});
targetP++;
#endif
*targetP++ = XA_LENGTH(d);
*targetP++ = XA_LIST_LENGTH(d);
memcpy(targetP, std_targets, sizeof(Atom) * std_length);
XtFree((char *) std_targets);
*type = XA_ATOM;
*format = 32;
result = True;
}
}
#if OPT_WIDE_CHARS
else if (screen->wide_chars && *target == XA_STRING) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XStringStyle);
} else if (screen->wide_chars && *target == XA_UTF8_STRING(d)) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XUTF8StringStyle);
} else if (screen->wide_chars && *target == XA_TEXT(d)) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XStdICCTextStyle);
} else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(d)) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
Xutf8TextListToTextProperty,
XCompoundTextStyle);
}
#endif
else if (*target == XA_STRING) {
*type = XA_STRING;
*value = screen->selection_data;
*length = screen->selection_length;
*format = 8;
result = True;
} else if (*target == XA_TEXT(d)) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
XmbTextListToTextProperty,
XStdICCTextStyle);
} else if (*target == XA_COMPOUND_TEXT(d)) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
XmbTextListToTextProperty,
XCompoundTextStyle);
}
#ifdef X_HAVE_UTF8_STRING
else if (*target == XA_UTF8_STRING(d)) {
result =
_ConvertSelectionHelper(w,
type, value, length, format,
XmbTextListToTextProperty,
XUTF8StringStyle);
}
#endif
else if (*target == XA_LIST_LENGTH(d)) {
*value = XtMalloc(4);
if (sizeof(long) == 4)
*(long *) *value = 1;
else {
long temp = 1;
memcpy((char *) *value, ((char *) &temp) + sizeof(long) - 4, 4);
}
*type = XA_INTEGER;
*length = 1;
*format = 32;
result = True;
} else if (*target == XA_LENGTH(d)) {
*value = XtMalloc(4);
if (sizeof(long) == 4)
*(long *) *value = screen->selection_length;
else {
long temp = screen->selection_length;
memcpy((char *) *value, ((char *) &temp) + sizeof(long) - 4, 4);
}
*type = XA_INTEGER;
*length = 1;
*format = 32;
result = True;
} else if (XmuConvertStandardSelection(w,
screen->selection_time, selection,
target, type, (XPointer *) value,
length, format)) {
result = True;
}
return result;
}
static void
LoseSelection(Widget w, Atom * selection)
{
TScreen *screen;
Atom *atomP;
Cardinal i;
if (!IsXtermWidget(w))
return;
screen = &((XtermWidget) w)->screen;
for (i = 0, atomP = screen->selection_atoms;
i < screen->selection_count; i++, atomP++) {
if (*selection == *atomP)
*atomP = (Atom) 0;
switch (*atomP) {
case XA_CUT_BUFFER0:
case XA_CUT_BUFFER1:
case XA_CUT_BUFFER2:
case XA_CUT_BUFFER3:
case XA_CUT_BUFFER4:
case XA_CUT_BUFFER5:
case XA_CUT_BUFFER6:
case XA_CUT_BUFFER7:
*atomP = (Atom) 0;
}
}
for (i = screen->selection_count; i; i--) {
if (screen->selection_atoms[i - 1] != 0)
break;
}
screen->selection_count = i;
for (i = 0, atomP = screen->selection_atoms;
i < screen->selection_count; i++, atomP++) {
if (*atomP == (Atom) 0) {
*atomP = screen->selection_atoms[--screen->selection_count];
}
}
if (screen->selection_count == 0)
TrackText(0, 0, 0, 0);
}
static void
SelectionDone(Widget w GCC_UNUSED,
Atom * selection GCC_UNUSED,
Atom * target GCC_UNUSED)
{
}
static void
_OwnSelection(XtermWidget termw,
String * selections,
Cardinal count)
{
Atom *atoms = termw->screen.selection_atoms;
Cardinal i;
Boolean have_selection = False;
if (termw->screen.selection_length < 0)
return;
if (count > termw->screen.sel_atoms_size) {
XtFree((char *) atoms);
atoms = (Atom *) XtMalloc(count * sizeof(Atom));
termw->screen.selection_atoms = atoms;
termw->screen.sel_atoms_size = count;
}
XmuInternStrings(XtDisplay((Widget) termw), selections, count, atoms);
for (i = 0; i < count; i++) {
int cutbuffer;
switch (atoms[i]) {
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;
}
if (cutbuffer >= 0) {
if (termw->screen.selection_length >
4 * XMaxRequestSize(XtDisplay((Widget) termw)) - 32) {
fprintf(stderr,
"%s: selection too big (%d bytes), not storing in CUT_BUFFER%d\n",
xterm_name, termw->screen.selection_length, cutbuffer);
} else {
unsigned long length = termw->screen.selection_length;
Char *data = termw->screen.selection_data;
if_OPT_WIDE_CHARS((&(termw->screen)), {
data = UTF8toLatin1(data, length, &length);
});
TRACE(("XStoreBuffer(%d)\n", cutbuffer));
XStoreBuffer(XtDisplay((Widget) termw),
(char *) data, length, cutbuffer);
}
} else if (!replyToEmacs) {
have_selection |=
XtOwnSelection((Widget) termw, atoms[i],
termw->screen.selection_time,
ConvertSelection, LoseSelection, SelectionDone);
}
}
if (!replyToEmacs)
termw->screen.selection_count = count;
if (!have_selection)
TrackText(0, 0, 0, 0);
}
void
DisownSelection(XtermWidget termw)
{
Atom *atoms = termw->screen.selection_atoms;
Cardinal count = termw->screen.selection_count;
Cardinal i;
for (i = 0; i < count; i++) {
int cutbuffer;
switch (atoms[i]) {
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;
}
if (cutbuffer < 0)
XtDisownSelection((Widget) termw, atoms[i],
termw->screen.selection_time);
}
termw->screen.selection_count = 0;
termw->screen.startHRow = termw->screen.startHCol = 0;
termw->screen.endHRow = termw->screen.endHCol = 0;
}
static int
Length(TScreen * screen GCC_UNUSED,
int row,
int scol,
int ecol)
{
int lastcol = LastTextCol(row);
if (ecol > lastcol)
ecol = lastcol;
return (ecol - scol + 1);
}
static Char *
SaveText(TScreen * screen,
int row,
int scol,
int ecol,
Char * lp,
int *eol)
{
int i = 0;
unsigned c;
Char *result = lp;
#if OPT_WIDE_CHARS
int previous = 0;
unsigned c_1 = 0, c_2 = 0;
#endif
i = Length(screen, row, scol, ecol);
ecol = scol + i;
#if OPT_DEC_CHRSET
if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, row + screen->topline)[0])) {
scol = (scol + 0) / 2;
ecol = (ecol + 1) / 2;
}
#endif
*eol = !ScrnTstWrapped(screen, row);
for (i = scol; i < ecol; i++) {
c = E2A(XTERM_CELL(row, i));
#if OPT_WIDE_CHARS
if (screen->utf8_mode) {
c_1 = E2A(XTERM_CELL_C1(row, i));
c_2 = E2A(XTERM_CELL_C2(row, i));
}
if (c == HIDDEN_CHAR && iswide(previous)) {
previous = c;
if (c_1) {
lp = convertToUTF8(lp, c_1);
if (c_2)
lp = convertToUTF8(lp, c_2);
}
continue;
}
previous = c;
if (screen->utf8_mode) {
lp = convertToUTF8(lp, c);
if (c_1) {
lp = convertToUTF8(lp, c_1);
if (c_2)
lp = convertToUTF8(lp, c_2);
}
} else
#endif
{
if (c == 0) {
c = E2A(' ');
} else if (c < E2A(' ')) {
if (c == XPOUND)
c = 0x23;
else
c += 0x5f;
} else if (c == 0x7f) {
c = 0x5f;
}
*lp++ = A2E(c);
}
if (c != E2A(' '))
result = lp;
}
if (!*eol || !screen->trim_selection)
result = lp;
return (result);
}
static int
BtnCode(XButtonEvent * event, int button)
{
int result = 32 + (KeyState(event->state) << 2);
if (button < 0 || button > 5) {
result += 3;
} else {
if (button > 3)
result += (64 - 4);
if (event->type == MotionNotify)
result += 32;
result += button;
}
return result;
}
#define MOUSE_LIMIT (255 - 32)
static void
EditorButton(XButtonEvent * event)
{
TScreen *screen = &term->screen;
int pty = screen->respond;
Char line[6];
int row, col;
int button, count = 0;
button = event->button - 1;
if (button >= 3)
button++;
row = (event->y - screen->border) / FontHeight(screen);
col = (event->x - OriginX(screen)) / FontWidth(screen);
if (row < 0)
row = 0;
else if (row > screen->max_row)
row = screen->max_row;
else if (row > MOUSE_LIMIT)
row = MOUSE_LIMIT;
if (col < 0)
col = 0;
else if (col > screen->max_col)
col = screen->max_col;
else if (col > MOUSE_LIMIT)
col = MOUSE_LIMIT;
if (screen->control_eight_bits) {
line[count++] = CSI;
} else {
line[count++] = ESC;
line[count++] = '[';
}
line[count++] = 'M';
if (screen->send_mouse_pos == X10_MOUSE) {
line[count++] = ' ' + button;
} else {
switch (event->type) {
case ButtonPress:
line[count++] = BtnCode(event, screen->mouse_button = button);
break;
case ButtonRelease:
if (button < 3)
button = -1;
line[count++] = BtnCode(event, screen->mouse_button = button);
break;
case MotionNotify:
if ((row == screen->mouse_row)
&& (col == screen->mouse_col))
return;
line[count++] = BtnCode(event, screen->mouse_button);
break;
default:
return;
}
}
screen->mouse_row = row;
screen->mouse_col = col;
line[count++] = ' ' + col + 1;
line[count++] = ' ' + row + 1;
TRACE(("mouse at %d,%d button+mask = %#x\n", row, col,
(screen->control_eight_bits) ? line[2] : line[3]));
v_write(pty, line, count);
}
#if OPT_TEK4014
void
HandleGINInput(Widget w GCC_UNUSED,
XEvent * event GCC_UNUSED,
String * param_list,
Cardinal * nparamsp)
{
if (term->screen.TekGIN && *nparamsp == 1) {
int c = param_list[0][0];
switch (c) {
case 'l':
case 'm':
case 'r':
case 'L':
case 'M':
case 'R':
break;
default:
Bell(XkbBI_MinorError, 0);
c = 'l';
}
TekEnqMouse(c | 0x80);
TekGINoff();
} else {
Bell(XkbBI_MinorError, 0);
}
}
#endif
void
HandleSecure(Widget w GCC_UNUSED,
XEvent * event,
String * params GCC_UNUSED,
Cardinal * param_count GCC_UNUSED)
{
Time ev_time = CurrentTime;
if ((event->xany.type == KeyPress) ||
(event->xany.type == KeyRelease))
ev_time = event->xkey.time;
else if ((event->xany.type == ButtonPress) ||
(event->xany.type == ButtonRelease))
ev_time = event->xbutton.time;
DoSecureKeyboard(ev_time);
}