summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan <scovich@gmail.com>2018-01-27 21:45:47 +0100
committerAmadeusz Sławiński <amade@asmblr.net>2018-02-07 00:17:07 +0100
commit40819ffe2b7ff2cbcb93d7b73d553179e57a27e1 (patch)
tree8b4098ed9232c51e7585ec063f25a6d70104e5c7
parent3e9aeb3316dfb2c9e7324e951d87e2bb83d1c07a (diff)
downloadscreen-40819ffe2b7ff2cbcb93d7b73d553179e57a27e1.tar.gz
support sgr mouse mose
Bug: #37206
-rw-r--r--src/ansi.c6
-rw-r--r--src/display.c328
-rw-r--r--src/display.h11
-rw-r--r--src/extern.h2
-rw-r--r--src/layer.c17
-rw-r--r--src/window.c1
-rw-r--r--src/window.h1
7 files changed, 318 insertions, 48 deletions
diff --git a/src/ansi.c b/src/ansi.c
index 6636d10..d2b3cbc 100644
--- a/src/ansi.c
+++ b/src/ansi.c
@@ -1471,6 +1471,12 @@ int c, intermediate;
curr->w_mouse = i ? a1 : 0;
LMouseMode(&curr->w_layer, curr->w_mouse);
break;
+ /* case 1005: UTF-8 mouse mode rejected */
+ case 1006: /* SGR mouse mode */
+ curr->w_extmouse = i ? a1 : 0;
+ LExtMouseMode(&curr->w_layer, curr->w_extmouse);
+ break;
+ /* case 1015: UXRVT mouse mode rejected */
}
}
break;
diff --git a/src/display.c b/src/display.c
index f4bcf89..190d418 100644
--- a/src/display.c
+++ b/src/display.c
@@ -39,11 +39,19 @@
#include "braille.h"
#include "canvas.h"
+/* CSI parsing status */
+enum
+ {
+ CSI_PB=0, CSI_PX=1, CSI_PY=2, CSI_DONE=3,
+ CSI_ESC_SEEN, CSI_BEGIN, CSI_INACTIVE, CSI_INVALID
+ };
+
static int CountChars __P((int));
static int DoAddChar __P((int));
static int BlankResize __P((int, int));
static int CallRewrite __P((int, int, int, int));
static void disp_readev_fn __P((struct event *, char *));
+static void disp_processinput __P((struct display *, unsigned char *, int));
static void disp_writeev_fn __P((struct event *, char *));
#ifdef linux
static void disp_writeev_eagain __P((struct event *, char *));
@@ -487,6 +495,7 @@ FinitTerm()
if (D_mousetrack)
D_mousetrack = 0;
MouseMode(0);
+ ExtMouseMode(0);
SetRendition(&mchar_null);
SetFlow(FLOW_NOW);
#ifdef MAPKEYS
@@ -833,6 +842,31 @@ int mode;
AddStr(mousebuf);
}
D_mouse = mode;
+ D_mouse_parse.state = CSI_INACTIVE;
+ }
+}
+
+void
+ExtMouseMode(mode)
+ int mode;
+{
+ if (display && D_extmouse != mode)
+ {
+ char mousebuf[20];
+ if (!D_CXT)
+ return;
+ if (D_extmouse)
+ {
+ sprintf(mousebuf, "\033[?%dl", D_extmouse);
+ AddStr(mousebuf);
+ }
+ if (mode)
+ {
+ sprintf(mousebuf, "\033[?%dh", mode);
+ AddStr(mousebuf);
+ }
+ D_extmouse = mode;
+ D_mouse_parse.state = CSI_INACTIVE;
}
}
@@ -1261,6 +1295,7 @@ int cur_only;
CursorkeysMode(0);
CursorVisibility(0);
MouseMode(0);
+ ExtMouseMode(0);
SetRendition(&mchar_null);
SetFlow(FLOW_NOW);
@@ -3159,6 +3194,7 @@ NukePending()
int oldkeypad = D_keypad, oldcursorkeys = D_cursorkeys;
int oldcurvis = D_curvis;
int oldmouse = D_mouse;
+ int oldextmouse = D_extmouse;
oldrend = D_rend;
len = D_obufp - D_obuf;
@@ -3221,6 +3257,7 @@ NukePending()
CursorkeysMode(oldcursorkeys);
CursorVisibility(oldcurvis);
MouseMode(oldmouse);
+ ExtMouseMode(oldextmouse);
if (D_CWS)
{
debug("ResizeDisplay: using WS\n");
@@ -3349,13 +3386,24 @@ char *data;
}
}
+/* maximum mouse sequence length is SGR: ESC [ < b ; x ; y M */
+#define MAX_MOUSE_SEQUENCE (3+10+1+10+1+10+1)
+
static void
disp_readev_fn(ev, data)
struct event *ev;
char *data;
{
+ /* We have to intercept mouse sequences in order to translate them
+ * properly, and an incoming sequence could be spread over multiple
+ * I/O reads. When this occurs, we buffer intermediate state in
+ * D_mouse_params, and then use the reservation at the front of
+ * bufspace to prepend the completed and translated mouse sequence.
+ */
int size;
- char buf[IOSIZE];
+ char bufspace[MAX_MOUSE_SEQUENCE + IOSIZE];
+ unsigned char *buf = bufspace + MAX_MOUSE_SEQUENCE;
+
struct canvas *cv;
display = (struct display *)data;
@@ -3442,63 +3490,247 @@ char *data;
ResetIdle();
if (D_fore)
D_fore->w_lastdisp = display;
+
if (D_mouse && D_forecv)
{
unsigned char *bp = (unsigned char *)buf;
- int x, y, i;
-
- /* XXX this assumes that the string is read in as a whole... */
- for (i = size; i > 0; i--, bp++)
+ unsigned char *end = bp + size;
+ unsigned char *mark = NULL;
+
+ /* When mouse mode is enabled, buffer up incoming CSI until we
+ * know whether it is a mouse sequence. If not a mouse event,
+ * emit the CSI unchanged; if an invalid mouse event, swallow
+ * it; otherwise, translate the sequence and emit it.
+ *
+ * Supported mouse events take two flavors.
+ *
+ * VT200: CSI M Cb Cx Cy
+ * SGR: CSI < Ps ; Ps ; Ps M|m
+ *
+ * UTF-8 and UXRVT modes are explicitly rejected because they
+ * introduce incompatibilities with other control sequences.
+ *
+ * NOTE: applications wishing to use SGR mode will normally
+ * enable VT200 mode first as a fallback in case the terminal
+ * does not support SGR mode. Thus, we must expect to receive
+ * either mode's sequences when mouse mode is enabled. We will
+ * dutifully translate whatever arrives, without attempting to
+ * convert between modes on behalf of the application.
+ */
+ switch (D_mouse_parse.state)
+ {
+ case CSI_PY:
+ case CSI_PX:
+ case CSI_PB:
+ /* Partial mouse sequence; do not restore suppressed
+ * characters. We will emit a translated version (if valid)
+ * or swallow them (otherwise).
+ */
+ break;
+
+ case CSI_BEGIN:
+ /* Partial CSI; restore suppressed characters in case it
+ * turns out not to be a mouse sequence after all.
+ */
+ *(--buf) = '[';
+ ++size;
+ /* fall through */
+
+ case CSI_ESC_SEEN:
+ /* Escape character; restore it in case this turns out not
+ * to be the start of a mouse sequence after all.
+ */
+ *(--buf) = '\033';
+ ++size;
+ break;
+
+ default:
+ break;
+ };
+
+ while (bp != end)
{
- if (i > 5 && bp[0] == 033 && bp[1] == '[' && bp[2] == 'M')
- {
- bp++;
- i--;
- }
- else if (i < 5 || bp[0] != 0233 || bp[1] != 'M')
- continue;
- x = bp[3] - 33;
- y = bp[4] - 33;
- if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye)
+ unsigned char c = *(bp++);
+
+ switch (D_mouse_parse.state)
{
- if ((D_fore && D_fore->w_mouse) || (D_mousetrack && D_forecv->c_layer->l_mode == 1))
- {
- /* Send clicks only if the window is expecting clicks */
- x -= D_forecv->c_xoff;
- y -= D_forecv->c_yoff;
- if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height)
+ case CSI_INACTIVE:
+ if (c == '\033')
+ {
+ /* potential escape sequence */
+ mark = bp-1;
+ D_mouse_parse.state = CSI_ESC_SEEN;
+ }
+ break;
+
+ case CSI_ESC_SEEN:
+ if (c == '[')
+ {
+ /* continue buffering an escape sequence */
+ D_mouse_parse.state = CSI_BEGIN;
+ }
+ else
+ D_mouse_parse.state = CSI_INACTIVE;
+ break;
+
+ case CSI_BEGIN:
+ if (c == 'M')
+ {
+ /* VT200 mouse sequence */
+ D_mouse_parse.state = CSI_PB;
+ D_mouse_parse.sgrmode = 0;
+ }
+ else if (c == '<')
+ {
+ /* SGR mouse sequence */
+ D_mouse_parse.state = CSI_PB;
+ D_mouse_parse.params[D_mouse_parse.state] = 0;
+ D_mouse_parse.sgrmode = 1;
+ }
+ else
+ D_mouse_parse.state = CSI_INACTIVE;
+ break;
+
+ case CSI_PB:
+ case CSI_PX:
+ case CSI_PY:
+ if (D_mouse_parse.sgrmode)
+ {
+ /* SGR mode: parse decimal numbers */
+ if ('0' <= c && c <= '9')
{
- bp[3] = x + 33;
- bp[4] = y + 33;
- i -= 4;
- bp += 4;
- continue;
- }
- }
- }
- else if (D_mousetrack && bp[2] == '#')
- {
- /* 'focus' to the clicked region, only on mouse up */
- struct canvas *cv = FindCanvas(x, y);
- if (cv)
- {
- SetForeCanvas(display, cv);
- /* XXX: Do we want to reset the input buffer? */
- }
+ D_mouse_parse.params[D_mouse_parse.state] *= 10;
+ D_mouse_parse.params[D_mouse_parse.state] += c - '0';
+ }
+ else if (D_mouse_parse.state == CSI_PY)
+ {
+ if (c == 'M' || c == 'm')
+ D_mouse_parse.state = CSI_DONE;
+ else
+ D_mouse_parse.state = CSI_INVALID;
+ }
+ else if (c == ';')
+ {
+ D_mouse_parse.state++;
+ D_mouse_parse.params[D_mouse_parse.state] = 0;
+ }
+ else
+ D_mouse_parse.state = CSI_INVALID;
+ }
+ else
+ {
+ /* VT200 mode: read raw binary values */
+ D_mouse_parse.params[D_mouse_parse.state++] = c;
+ }
+ break;
+
+ default:
+ break;
}
- if (bp[0] == '[')
+
+ if (D_mouse_parse.state == CSI_INVALID)
+ {
+ /* swallow invalid sequence, but emit whatever came before */
+ if (buf < mark)
+ disp_processinput(display, buf, mark-buf);
+
+ buf = bp;
+ size = end - bp;
+ D_mouse_parse.state = CSI_INACTIVE;
+ }
+ else if (D_mouse_parse.state == CSI_DONE)
{
- bcopy((char *)bp + 1, (char *)bp, i);
- bp--;
- size--;
- }
- if (i > 5)
- bcopy((char *)bp + 5, (char *)bp, i - 5);
- bp--;
- i -= 4;
- size -= 5;
+ /* emit whatever came before this sequence */
+ if (buf < mark)
+ disp_processinput(display, buf, mark-buf);
+
+ buf = bp;
+ size = end - bp;
+
+ int x = D_mouse_parse.params[CSI_PX];
+ int y = D_mouse_parse.params[CSI_PY];
+ int bias = D_mouse_parse.sgrmode? 1 : 33;
+
+ x -= bias;
+ y -= bias;
+
+ if (x >= D_forecv->c_xs && x <= D_forecv->c_xe && y >= D_forecv->c_ys && y <= D_forecv->c_ye)
+ {
+ if ((D_fore && D_fore->w_mouse) || (D_mousetrack && D_forecv->c_layer->l_mode == 1))
+ {
+ /* Send clicks only if the window is expecting clicks */
+ x -= D_forecv->c_xoff;
+ y -= D_forecv->c_yoff;
+
+ if (x >= 0 && x < D_forecv->c_layer->l_width && y >= 0 && y < D_forecv->c_layer->l_height)
+ {
+ char tmp[MAX_MOUSE_SEQUENCE+1];
+ int n;
+
+ x += bias;
+ y += bias;
+
+ if (D_mouse_parse.sgrmode)
+ {
+ n = snprintf(
+ tmp, MAX_MOUSE_SEQUENCE, "\033[<%d;%d;%d%c",
+ D_mouse_parse.params[CSI_PB], x, y, c);
+ }
+ else
+ {
+ n = snprintf(
+ tmp, MAX_MOUSE_SEQUENCE, "\033[M%c%c%c",
+ D_mouse_parse.params[CSI_PB], x, y);
+ }
+
+ if (n > MAX_MOUSE_SEQUENCE)
+ n = MAX_MOUSE_SEQUENCE;
+
+ /* emit sequence */
+ buf -= n;
+ size += n;
+ memcpy(buf, tmp, n);
+ }
+ }
+ }
+ else if (D_mousetrack)
+ {
+ /* 'focus' to the clicked region, only on mouse up */
+ int focus = 0;
+ if (D_mouse_parse.sgrmode)
+ focus = (c == 'm');
+ else
+ focus = (D_mouse_parse.params[CSI_PB] == '#');
+
+ struct canvas *cv = FindCanvas(x, y);
+ if (focus && cv)
+ {
+ SetForeCanvas(display, cv);
+ /* XXX: Do we want to reset the input buffer? */
+ }
+ }
+
+ D_mouse_parse.state = CSI_INACTIVE;
+ }
}
+
+ if (D_mouse_parse.state != CSI_INACTIVE)
+ {
+ /* suppress partial sequence at end */
+ size = mark? mark - buf : 0;
+ }
}
+
+ if (size > 0)
+ disp_processinput(display, buf, size);
+}
+
+static void
+disp_processinput(display, buf, size)
+ struct display* display;
+ unsigned char* buf;
+ int size;
+{
#ifdef ENCODINGS
if (D_encoding != (D_forecv ? D_forecv->c_layer->l_encoding : 0))
{
diff --git a/src/display.h b/src/display.h
index 19dded7..f359328 100644
--- a/src/display.h
+++ b/src/display.h
@@ -58,6 +58,13 @@ struct kmap_ext
struct win; /* forward declaration */
+struct mouse_parse
+{
+ char sgrmode; /* non-zero if parsing an SGR sequence */
+ char state; /* current state of parsing */
+ int params[3]; /* parsed params: button, x, y */
+};
+
struct display
{
struct display *d_next; /* linked list */
@@ -100,6 +107,8 @@ struct display
int d_hstatus; /* hardstatus used */
int d_lp_missing; /* last character on bot line missing */
int d_mouse; /* mouse mode */
+ int d_extmouse; /* extended mouse mode */
+ struct mouse_parse d_mouse_parse; /* state of mouse code parsing */
int d_mousetrack; /* set when user wants to use mouse even when the window
does not */
#ifdef RXVT_OSC
@@ -227,6 +236,8 @@ extern struct display TheDisplay;
#define D_hstatus DISPLAY(d_hstatus)
#define D_lp_missing DISPLAY(d_lp_missing)
#define D_mouse DISPLAY(d_mouse)
+#define D_mouse_parse DISPLAY(d_mouse_parse)
+#define D_extmouse DISPLAY(d_extmouse)
#define D_mousetrack DISPLAY(d_mousetrack)
#define D_xtermosc DISPLAY(d_xtermosc)
#define D_lpchar DISPLAY(d_lpchar)
diff --git a/src/extern.h b/src/extern.h
index 55e007d..0701bf5 100644
--- a/src/extern.h
+++ b/src/extern.h
@@ -289,6 +289,7 @@ extern void CursorkeysMode __P((int));
extern void ReverseVideo __P((int));
extern void CursorVisibility __P((int));
extern void MouseMode __P((int));
+extern void ExtMouseMode __P((int));
extern void SetFont __P((int));
extern void SetAttr __P((int));
extern void SetColor __P((int, int));
@@ -449,6 +450,7 @@ extern void LSetFlow __P((struct layer *, int));
extern void LKeypadMode __P((struct layer *, int));
extern void LCursorkeysMode __P((struct layer *, int));
extern void LMouseMode __P((struct layer *, int));
+extern void LExtMouseMode __P((struct layer *, int));
#if defined(USEVARARGS)
extern void LMsg __P((int, const char *, ...)) __attribute__((format(printf, 2, 3)));
#else
diff --git a/src/layer.c b/src/layer.c
index fc8d03b..f6d4557 100644
--- a/src/layer.c
+++ b/src/layer.c
@@ -904,6 +904,23 @@ VA_DECL
}
}
+void
+LExtMouseMode(l, on)
+struct layer *l;
+int on;
+{
+ struct canvas *cv;
+ for (cv = l->l_cvlist; cv; cv = cv->c_lnext)
+ {
+ display = cv->c_display;
+ if (D_blocked)
+ continue;
+ if (cv != D_forecv)
+ continue;
+ ExtMouseMode(on);
+ }
+}
+
/*******************************************************************/
/*******************************************************************/
diff --git a/src/window.c b/src/window.c
index 3a64b26..2354be9 100644
--- a/src/window.c
+++ b/src/window.c
@@ -508,6 +508,7 @@ WinRestore()
ReverseVideo(fore->w_revvid);
CursorVisibility(fore->w_curinv ? -1 : fore->w_curvvis);
MouseMode(fore->w_mouse);
+ ExtMouseMode(fore->w_extmouse);
}
}
diff --git a/src/window.h b/src/window.h
index bd10dcd..99f44c0 100644
--- a/src/window.h
+++ b/src/window.h
@@ -240,6 +240,7 @@ struct win
char w_xtermosc[4][MAXSTR]; /* special xterm/rxvt escapes */
#endif
int w_mouse; /* mouse mode 0,9,1000 */
+ int w_extmouse; /* extended mouse mode 0,1006 */
#ifdef HAVE_BRAILLE
int w_bd_x, w_bd_y; /* Braille cursor position */
#endif