1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-20 00:15:31 +00:00

[mouse] Decode ESC [ < . Refs #246

It is based on links code. Was not tested too much.
This commit is contained in:
Witold Filipczyk 2023-07-18 17:13:58 +02:00
parent 61ceecdbd8
commit e4fb9a80df
3 changed files with 96 additions and 68 deletions

View File

@ -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;
}

View File

@ -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[<b;x;yM where b, x and y are in decimal, no longer offset by 32,
and the trailing letter is 'm' instead of 'M' for mouse release so that the released button is reported. */
int eel;
int x = 0, y = 0, b = 0;
unsigned char 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;
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;
}

View File

@ -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
}