diff options
author | Ryan <scovich@gmail.com> | 2018-01-27 21:45:47 +0100 |
---|---|---|
committer | Amadeusz Sławiński <amade@asmblr.net> | 2018-02-07 00:17:07 +0100 |
commit | 40819ffe2b7ff2cbcb93d7b73d553179e57a27e1 (patch) | |
tree | 8b4098ed9232c51e7585ec063f25a6d70104e5c7 | |
parent | 3e9aeb3316dfb2c9e7324e951d87e2bb83d1c07a (diff) | |
download | screen-40819ffe2b7ff2cbcb93d7b73d553179e57a27e1.tar.gz |
support sgr mouse mose
Bug: #37206
-rw-r--r-- | src/ansi.c | 6 | ||||
-rw-r--r-- | src/display.c | 328 | ||||
-rw-r--r-- | src/display.h | 11 | ||||
-rw-r--r-- | src/extern.h | 2 | ||||
-rw-r--r-- | src/layer.c | 17 | ||||
-rw-r--r-- | src/window.c | 1 | ||||
-rw-r--r-- | src/window.h | 1 |
7 files changed, 318 insertions, 48 deletions
@@ -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 |