From e4fb9a80dfbdd5ddb56c265e8605bab62b34852f Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Tue, 18 Jul 2023 17:13:58 +0200 Subject: [PATCH] [mouse] Decode ESC [ < . Refs #246 It is based on links code. Was not tested too much. --- src/terminal/kbd.c | 88 ++++++++++++-------------------------------- src/terminal/mouse.c | 75 +++++++++++++++++++++++++++++++++++-- src/terminal/mouse.h | 1 + 3 files changed, 96 insertions(+), 68 deletions(-) diff --git a/src/terminal/kbd.c b/src/terminal/kbd.c index 3396345f..b680ca82 100644 --- a/src/terminal/kbd.c +++ b/src/terminal/kbd.c @@ -733,70 +733,20 @@ free_and_return: free_itrm(itrm); } - -/** Parse an ECMA-48 control sequence that was received from a - * terminal. Extract the Final Byte (if there are no Intermediate - * Bytes) and the value of the first parameter (if it is an integer). - * - * This function assumes the control sequence begins with a CSI - - * CONTROL SEQUENCE INTRODUCER encoded as ESC [. (ECMA-48 also allows - * 0x9B as a single-byte CSI, but we don't support that here.) - * - * @returns one of: - * - -1 if the control sequence is not yet complete; the caller sets a timer. - * - 0 if the control sequence does not comply with ECMA-48. - * - The length of the control sequence otherwise. */ -static inline int -get_esc_code(unsigned char *str, int len, char *final_byte, - int *first_param_value) +static int +get_esc_code(unsigned char *str, int len, unsigned char *code, int *num, int *el) { - const int parameter_pos = 2; - int intermediate_pos; - int final_pos; int pos; - - *final_byte = '\0'; - *first_param_value = 0; - - /* Parameter Bytes */ - pos = parameter_pos; - while (pos < len && str[pos] >= 0x30 && str[pos] <= 0x3F) - ++pos; - - /* Intermediate Bytes */ - intermediate_pos = pos; - while (pos < len && str[pos] >= 0x20 && str[pos] <= 0x2F) - ++pos; - - /* Final Byte */ - final_pos = pos; - if (pos >= len) - return -1; - if (!(str[pos] >= 0x40 && str[pos] <= 0x7E)) - return 0; - - /* The control sequence seems OK. If the first Parameter - * Byte indicates that the parameter string is formatted - * as specified in clause 5.4.2 of ECMA-48, and the first - * parameter is an integer, then compute its value. - * (We need not check @len here because the loop cannot get - * past the Final Byte.) */ - for (pos = parameter_pos; str[pos] >= 0x30 && str[pos] <= 0x39; ++pos) - *first_param_value = *first_param_value * 10 + str[pos] - 0x30; - /* If the first parameter contains an embedded separator, then - * the value is not an integer, so discard what we computed. */ - if (str[pos] == 0x3A) - *first_param_value = 0; - - /* The meaning of the Final Byte depends on the Intermediate - * Bytes. Because we don't currently need to recognize any - * control sequences that use Intermediate Bytes, we just - * discard the Final Byte if there are any Intermediate - * Bytes. */ - if (intermediate_pos == final_pos) - *final_byte = str[final_pos]; - - return final_pos + 1; + *num = 0; + for (pos = 2; pos < len; pos++) { + if (str[pos] < '0' || str[pos] > '9' || pos > 7) { + *el = pos + 1; + *code = str[pos]; + return 0; + } + *num = *num * 10 + str[pos] - '0'; + } + return -1; } /* Define it to dump queue content in a readable form, @@ -830,9 +780,10 @@ static int decode_terminal_escape_sequence(struct itrm *itrm, struct interlink_event *ev) { struct term_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE }; - char c; + unsigned char c; int v; int el; + int res; if (itrm->in.queue.len == 2 && itrm->in.queue.data[1] == ASCII_ESC && get_ui_double_esc()) { kbd.key = KBD_ESC; @@ -857,8 +808,9 @@ decode_terminal_escape_sequence(struct itrm *itrm, struct interlink_event *ev) return -1; } - el = get_esc_code(itrm->in.queue.data, itrm->in.queue.len, &c, &v); - if (el == -1) { + res = get_esc_code(itrm->in.queue.data, itrm->in.queue.len, &c, &v, &el); + + if (res == -1) { /* If the control sequence is incomplete but itrm->in.queue * is already full, then we must not wait for more input: * kbd_timeout might call in_kbd and thus process_input @@ -982,6 +934,12 @@ decode_terminal_escape_sequence(struct itrm *itrm, struct interlink_event *ev) case 'M': /* (DL) kmous xterm */ #ifdef CONFIG_MOUSE el = decode_terminal_mouse_escape_sequence(itrm, ev, el, v); +#endif /* CONFIG_MOUSE */ + break; + + case '<': /* (DL) kmous xterm */ +#ifdef CONFIG_MOUSE + el = decode_terminal_mouse_escape_sequence_256(itrm, ev, el, v); #endif /* CONFIG_MOUSE */ break; } diff --git a/src/terminal/mouse.c b/src/terminal/mouse.c index b51aaa0c..84dea375 100644 --- a/src/terminal/mouse.c +++ b/src/terminal/mouse.c @@ -50,7 +50,7 @@ extern struct itrm *ditrm; #define INIT_TWIN_MOUSE_SEQ "\033[?9h" /**< Send MIT Mouse Row & Column on Button Press */ -#define INIT_XWIN_MOUSE_SEQ "\033[?1000h" /**< Send Mouse X & Y on button press and release */ +#define INIT_XWIN_MOUSE_SEQ "\033[?1000h\033[?1002h\033[?1005l\033[?1015l\033[?1006h" /**< Send Mouse X & Y on button press and release */ void send_mouse_init_sequence(int h) @@ -60,7 +60,7 @@ send_mouse_init_sequence(int h) } #define DONE_TWIN_MOUSE_SEQ "\033[?9l" /**< Don't Send MIT Mouse Row & Column on Button Press */ -#define DONE_XWIN_MOUSE_SEQ "\033[?1000l" /**< Don't Send Mouse X & Y on button press and release */ +#define DONE_XWIN_MOUSE_SEQ "\033[?1000l\033[?1002l\033[?1006l" /**< Don't Send Mouse X & Y on button press and release */ void send_mouse_done_sequence(int h) @@ -152,13 +152,14 @@ decode_mouse_position(struct itrm *itrm, int from) #define TW_BUTT_MIDDLE 2 #define TW_BUTT_RIGHT 4 +static int xterm_button = -1; + /** @returns length of the escape sequence or -1 if the caller needs to set up * the ESC delay timer. */ int decode_terminal_mouse_escape_sequence(struct itrm *itrm, struct interlink_event *ev, int el, int v) { - static int xterm_button = -1; struct interlink_event_mouse mouse; if (itrm->in.queue.len - el < 3) @@ -236,3 +237,71 @@ decode_terminal_mouse_escape_sequence(struct itrm *itrm, struct interlink_event return el; } + +int +decode_terminal_mouse_escape_sequence_256(struct itrm *itrm, struct interlink_event *ev, + int el, int v) +{ + struct interlink_event_mouse mouse; + /* SGR 1006 mouse extension: \e[in.queue.len) return -1; + if (el - eel >= 9) return el; + ch = itrm->in.queue.data[el++]; + if (ch == ';') break; + if (ch < '0' || ch > '9') return el; + b = 10 * b + (ch - '0'); + } + eel = el; + + while (1) { + if (el == itrm->in.queue.len) return -1; + if (el - eel >= 9) return el; + ch = itrm->in.queue.data[el++]; + if (ch == ';') break; + if (ch < '0' || ch > '9') return el; + x = 10 * x + (ch - '0'); + } + eel = el; + + while (1) { + if (el == itrm->in.queue.len) return -1; + if (el - eel >= 9) return el; + ch = itrm->in.queue.data[el++]; + if (ch == 'M' || ch == 'm') break; + if (ch < '0' || ch > '9') return el; + y = 10 * y + (ch - '0'); + } + + mouse.x = x - 1; + mouse.y = y - 1; + + if (ch == 'm') mouse.button = B_UP; + else if ((b & 0x20) == 0x20) mouse.button = B_DRAG, b &= ~0x20; + else mouse.button = B_DOWN; + + if (b == 0) mouse.button |= B_LEFT; + else if (b == 1) mouse.button |= B_MIDDLE; + else if (b == 2) mouse.button |= B_RIGHT; + else if (b == 3 && xterm_button >= 0) mouse.button |= xterm_button; + else if (b == 0x40) mouse.button |= B_WHEEL_UP; + else if (b == 0x41) mouse.button |= B_WHEEL_DOWN; + + + xterm_button = -1; + if (mouse_action_is(&mouse, B_DOWN)) { + xterm_button = mouse_get_button(&mouse); + } + + /* Postpone changing of the event type until all sanity + * checks have been done. */ + set_mouse_interlink_event(ev, mouse.x, mouse.y, mouse.button); + + return el; +} diff --git a/src/terminal/mouse.h b/src/terminal/mouse.h index 653daaa8..597d2a28 100644 --- a/src/terminal/mouse.h +++ b/src/terminal/mouse.h @@ -126,6 +126,7 @@ void disable_mouse(void); void enable_mouse(void); void toggle_mouse(struct session *ses); int decode_terminal_mouse_escape_sequence(struct itrm *itrm, struct interlink_event *ev, int el, int v); +int decode_terminal_mouse_escape_sequence_256(struct itrm *itrm, struct interlink_event *ev, int el, int v); #ifdef __cplusplus }