summaryrefslogtreecommitdiff
path: root/src/gui/curses
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2024-02-24 21:31:48 +0100
committerSébastien Helleu <flashcode@flashtux.org>2024-03-06 20:33:17 +0100
commit5c88ee9c45411ae22b16d66d92c48323546aa761 (patch)
treec2ea0182c4c759c387aa8ebdffc9c4cca299e759 /src/gui/curses
parenta8a005321181df14f78f1213c3d8e20e9b59090d (diff)
downloadweechat-5c88ee9c45411ae22b16d66d92c48323546aa761.zip
core: add support of SGR mouse events, remove option weechat.look.mouse_timer_delay (closes #2082)
Diffstat (limited to 'src/gui/curses')
-rw-r--r--src/gui/curses/gui-curses-key.c16
-rw-r--r--src/gui/curses/gui-curses-mouse.c450
-rw-r--r--src/gui/curses/gui-curses-mouse.h7
3 files changed, 314 insertions, 159 deletions
diff --git a/src/gui/curses/gui-curses-key.c b/src/gui/curses/gui-curses-key.c
index c20e417fb..2c1f7acf9 100644
--- a/src/gui/curses/gui-curses-key.c
+++ b/src/gui/curses/gui-curses-key.c
@@ -331,14 +331,7 @@ gui_key_flush (int paste)
insert_ok = 1;
utf_partial_char[0] = '\0';
- if (!paste && gui_mouse_event_pending)
- {
- insert_ok = 0;
- key_str[0] = (char)key;
- key_str[1] = '\0';
- length_key_str = 1;
- }
- else if (!paste && (key < 32))
+ if (!paste && (key < 32))
{
insert_ok = 0;
key_str[0] = '\x01';
@@ -353,6 +346,13 @@ gui_key_flush (int paste)
key_str[2] = '\0';
length_key_str = 2;
}
+ else if (!paste && gui_mouse_event_pending)
+ {
+ insert_ok = 0;
+ key_str[0] = (char)key;
+ key_str[1] = '\0';
+ length_key_str = 1;
+ }
else if (!paste && (key == 127))
{
insert_ok = 0;
diff --git a/src/gui/curses/gui-curses-mouse.c b/src/gui/curses/gui-curses-mouse.c
index caa2aab1c..5f282b988 100644
--- a/src/gui/curses/gui-curses-mouse.c
+++ b/src/gui/curses/gui-curses-mouse.c
@@ -23,8 +23,10 @@
#include "config.h"
#endif
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <ctype.h>
#include <math.h>
#include "../../core/weechat.h"
@@ -46,7 +48,7 @@
#include "gui-curses-mouse.h"
-char *gui_mouse_wheel_codes[][2] =
+char *gui_mouse_wheel_utf8_codes[][2] =
{ { "`", "wheelup" },
{ "p", "ctrl-wheelup" },
{ "h", "alt-wheelup" },
@@ -57,7 +59,7 @@ char *gui_mouse_wheel_codes[][2] =
{ "y", "alt-ctrl-wheeldown" },
{ NULL, NULL } };
-char *gui_mouse_button_codes[][2] =
+char *gui_mouse_button_utf8_codes[][2] =
{ { " ", "button1" },
{ "\"", "button2" },
{ "!", "button3" },
@@ -87,7 +89,7 @@ void
gui_mouse_enable ()
{
gui_mouse_enabled = 1;
- fprintf (stderr, "\033[?1005h\033[?1000h\033[?1002h");
+ fprintf (stderr, "\033[?1005h\033[?1006h\033[?1000h\033[?1002h");
fflush (stderr);
(void) hook_signal_send ("mouse_enabled",
@@ -102,7 +104,7 @@ void
gui_mouse_disable ()
{
gui_mouse_enabled = 0;
- fprintf (stderr, "\033[?1002l\033[?1000l\033[?1005l");
+ fprintf (stderr, "\033[?1002l\033[?1000l\033[?1006l\033[?1005l");
fflush (stderr);
(void) hook_signal_send ("mouse_disabled",
@@ -212,87 +214,321 @@ gui_mouse_grab_end (const char *mouse_key)
}
/*
- * Timer for grabbing mouse code.
+ * Returns size of mouse event (SGR and UTF-8 events are supported):
+ * -1: not a mouse event
+ * 0: incomplete mouse event
+ * > 0: complete mouse event
*/
int
-gui_mouse_event_timer_cb (const void *pointer, void *data, int remaining_calls)
+gui_mouse_event_size (const char *key)
{
- /* make C compiler happy */
- (void) pointer;
- (void) data;
- (void) remaining_calls;
+ const char *ptr_key;
+ int i, key_valid_utf8;
- gui_mouse_event_end ();
+ if (!key || !key[0])
+ return -1;
- return WEECHAT_RC_OK;
+ if (strncmp (key, "\x01[[<", 4) == 0)
+ {
+ /*
+ * SGR event: digits separated by ';', ending by 'M' (pressed)
+ * or 'm' (released), example: "0;71;21M"
+ */
+ for (ptr_key = key + 4; ptr_key[0]; ptr_key++)
+ {
+ if ((ptr_key[0] != ';') && !isdigit ((unsigned char)ptr_key[0]))
+ return ptr_key - key + 1;
+ }
+ return 0;
+ }
+ else if (strncmp (key, "\x01[[M", 4) == 0)
+ {
+ /* UTF-8 event: 3 UTF-8 chars, example: "!4&" */
+ ptr_key = key + 4;
+ key_valid_utf8 = utf8_is_valid (ptr_key, -1, NULL);
+ for (i = 0; i < 3; i++)
+ {
+ if (!ptr_key[0])
+ return 0;
+ ptr_key = (key_valid_utf8) ? utf8_next_char (ptr_key) : ptr_key + 1;
+ if (!ptr_key)
+ return 0;
+ }
+ return ptr_key - key;
+ }
+
+ /* invalid mouse event, or not supported */
+ return -1;
}
/*
- * Initializes mouse event.
+ * Concatenates the mouse event gesture in a key (containing the button name).
+ *
+ * Note: *key must be long enough for the gesture added by this function
+ * (see below).
+ *
+ * Mouse gesture: if (x,y) on release is different from (x,y) on click,
+ * compute distance and angle between 2 points.
+ *
+ * Distance: sqrt((x2-x1)²+(y2-y1)²)
+ * Angle : atan2(x1-x1, y2-y1)
+ *
+ * Angle:
+ *
+ * 3.14 pi
+ * /\
+ * -2.35 || 2.35 3/4 * pi
+ * ||
+ * -1.57 /----++----\ 1.57 1/2 * pi
+ * \----++----/
+ * ||
+ * -0.78 || 0.78 1/4 * pi
+ * \/
+ * 0.00 0
+ *
+ * Possible returned gestures are:
+ *
+ * value returned | dist. | angle
+ * ----------------------+-------+--------------------------
+ * "-gesture-up" | 3..19 | -2.35..-3.14 + 2.35..3.14
+ * "-gesture-up-long" | >= 20 |
+ * "-gesture-down" | 3..19 | -0.78..0.78
+ * "-gesture-down-long" | >= 20 |
+ * "-gesture-left" | 3..39 | -0.78..-2.35
+ * "-gesture-left-long" | >= 40 |
+ * "-gesture-right" | 3..39 | 0.78..2.35
+ * "-gesture-right-long" | >= 40 |
*/
void
-gui_mouse_event_init ()
+gui_mouse_event_concat_gesture (char *key)
{
- gui_mouse_event_pending = 1;
+ double diff_x, diff_y, distance, angle, pi4;
- if (gui_mouse_event_timer)
- unhook (gui_mouse_event_timer);
+ /* (x, y) == (x2, y2) => no gesture, do not add anything */
+ if ((gui_mouse_event_x[0] == gui_mouse_event_x[1])
+ && (gui_mouse_event_y[0] == gui_mouse_event_y[1]))
+ return;
- gui_mouse_event_timer = hook_timer (NULL,
- CONFIG_INTEGER(config_look_mouse_timer_delay),
- 0, 1,
- &gui_mouse_event_timer_cb, NULL, NULL);
+ diff_x = gui_mouse_event_x[1] - gui_mouse_event_x[0];
+ diff_y = gui_mouse_event_y[1] - gui_mouse_event_y[0];
+ distance = sqrt ((diff_x * diff_x) + (diff_y * diff_y));
+ if (distance >= 3)
+ {
+ angle = atan2 ((double)(gui_mouse_event_x[1] - gui_mouse_event_x[0]),
+ (double)(gui_mouse_event_y[1] - gui_mouse_event_y[0]));
+ pi4 = 3.14159265358979 / 4;
+ if ((angle <= pi4 * (-3)) || (angle >= pi4 * 3))
+ {
+ strcat (key, "-gesture-up");
+ if (distance >= 20)
+ strcat (key, "-long");
+ }
+ else if ((angle >= pi4 * (-1)) && (angle <= pi4))
+ {
+ strcat (key, "-gesture-down");
+ if (distance >= 20)
+ strcat (key, "-long");
+ }
+ else if ((angle >= pi4 * (-3)) && (angle <= pi4 * (-1)))
+ {
+ strcat (key, "-gesture-left");
+ if (distance >= 40)
+ strcat (key, "-long");
+ }
+ else if ((angle >= pi4) && (angle <= pi4 * 3))
+ {
+ strcat (key, "-gesture-right");
+ if (distance >= 40)
+ strcat (key, "-long");
+ }
+ }
}
/*
- * Gets key name with a mouse code.
+ * Gets mouse event name with a SGR mouse event.
*/
const char *
-gui_mouse_event_code2key (const char *code)
+gui_mouse_event_name_sgr (const char *key)
{
- int i, x, y, code_utf8, length;
- double diff_x, diff_y, distance, angle, pi4;
- static char key[128];
- const char *ptr_code;
+ int length, num_items, is_release;
+ char **items, *error;
+ static char mouse_key[128];
+ long button, x, y;
+
+ if (!key || !key[0])
+ return NULL;
+
+ mouse_key[0] = '\0';
+
+ items = string_split (key, ";", NULL, 0, 0, &num_items);
+ if (!items)
+ return NULL;
+
+ if (num_items < 3)
+ goto error;
+
+ error = NULL;
+ button = strtol (items[0], &error, 10);
+ if (!error || error[0])
+ goto error;
+
+ error = NULL;
+ x = strtol (items[1], &error, 10);
+ if (!error || error[0])
+ goto error;
+ x = (x >= 1) ? x - 1 : 0;
+
+ length = (int)strlen (items[2]);
+ if (length <= 0)
+ goto error;
+ is_release = (items[2][length - 1] == 'm') ? 1 : 0;
+ items[2][length - 1] = '\0';
+ error = NULL;
+ y = strtol (items[2], &error, 10);
+ if (!error || error[0])
+ goto error;
+ y = (y >= 1) ? y - 1 : 0;
+
+ /* set data in "gui_mouse_event_xxx" */
+ gui_mouse_event_x[gui_mouse_event_index] = x;
+ gui_mouse_event_y[gui_mouse_event_index] = y;
+
+ /* keep same coordinates if it's release code received as first event */
+ if ((gui_mouse_event_index == 0) && is_release)
+ {
+ gui_mouse_event_index = 1;
+ gui_mouse_event_x[1] = gui_mouse_event_x[0];
+ gui_mouse_event_y[1] = gui_mouse_event_y[0];
+ }
+
+ if (gui_mouse_event_index == 0)
+ gui_mouse_event_index = 1;
+
+ if (button & 8)
+ strcat (mouse_key, "alt-");
+ if (button & 16)
+ strcat (mouse_key, "ctrl-");
+ if (button & 4)
+ strcat (mouse_key, "shift-");
+
+ if (button & 64)
+ {
+ if ((button & 3) == 0)
+ strcat (mouse_key, "wheelup");
+ else if ((button & 3) == 1)
+ strcat (mouse_key, "wheeldown");
+ /* not yet supported: wheel left/right */
+ /*
+ else if ((button & 3) == 2)
+ strcat (mouse_key, "wheelleft");
+ else if ((button & 3) == 3)
+ strcat (mouse_key, "wheelright");
+ */
+ gui_mouse_event_x[1] = gui_mouse_event_x[0];
+ gui_mouse_event_y[1] = gui_mouse_event_y[0];
+ goto end;
+ }
+ else if (button & 128)
+ {
+ if ((button & 3) == 0)
+ strcat (mouse_key, "button8");
+ else if ((button & 3) == 1)
+ strcat (mouse_key, "button9");
+ else if ((button & 3) == 2)
+ strcat (mouse_key, "button10");
+ else if ((button & 3) == 3)
+ strcat (mouse_key, "button11");
+ }
+ else
+ {
+ if ((button & 3) == 0)
+ strcat (mouse_key, "button1");
+ else if ((button & 3) == 1)
+ strcat (mouse_key, "button3");
+ else if ((button & 3) == 2)
+ strcat (mouse_key, "button2");
+ }
+
+ if (!is_release && !(button & 64))
+ {
+ strcat (mouse_key, "-event-");
+ if (button & 32)
+ {
+ strcat (mouse_key, "drag");
+ }
+ else
+ {
+ gui_mouse_event_x[1] = gui_mouse_event_x[0];
+ gui_mouse_event_y[1] = gui_mouse_event_y[0];
+ strcat (mouse_key, "down");
+ }
+ goto end;
+ }
+
+ gui_mouse_event_concat_gesture (mouse_key);
+
+end:
+ string_free_split (items);
+ return mouse_key;
- key[0] = '\0';
+error:
+ string_free_split (items);
+ return NULL;
+}
+
+/*
+ * Gets mouse event name with a UTF-8 mouse event: if the key is invalid UTF-8,
+ * use the 3 bytes, otherwise 3 UTF-8 chars.
+ */
+
+const char *
+gui_mouse_event_name_utf8 (const char *key)
+{
+ int i, x, y, key_valid_utf8, length;
+ static char mouse_key[128];
+ const char *ptr_key;
+
+ if (!key || !key[0])
+ return NULL;
+
+ mouse_key[0] = '\0';
/*
- * mouse code must have at least:
+ * key must have at least:
* one code (for event) + X + Y == 3 bytes or 3 UTF-8 chars
*/
- code_utf8 = utf8_is_valid (code, -1, NULL);
- length = (code_utf8) ? utf8_strlen (code) : (int)strlen (code);
+ key_valid_utf8 = utf8_is_valid (key, -1, NULL);
+ length = (key_valid_utf8) ? utf8_strlen (key) : (int)strlen (key);
if (length < 3)
return NULL;
/* get coordinates and button */
- if (code_utf8)
+ if (key_valid_utf8)
{
- /* get coordinates using UTF-8 chars in code */
- x = utf8_char_int (code + 1) - 33;
- ptr_code = utf8_next_char (code + 1);
- if (!ptr_code)
+ /* get coordinates using UTF-8 chars in key */
+ x = utf8_char_int (key + 1) - 33;
+ ptr_key = (char *)utf8_next_char (key + 1);
+ if (!ptr_key)
return NULL;
- y = utf8_char_int (ptr_code) - 33;
+ y = utf8_char_int (ptr_key) - 33;
}
else
{
- /* get coordinates using ISO chars in code */
- x = ((unsigned char)code[1]) - 33;
- y = ((unsigned char)code[2]) - 33;
+ /* get coordinates using ISO chars in key */
+ x = ((unsigned char)key[1]) - 33;
+ y = ((unsigned char)key[2]) - 33;
}
if (x < 0)
x = 0;
if (y < 0)
y = 0;
- /* ignore code if it's motion/end code received as first event */
+ /* ignore key if it's motion/end code received as first event */
if ((gui_mouse_event_index == 0)
- && (MOUSE_CODE_MOTION(code[0]) || MOUSE_CODE_END(code[0])))
+ && (MOUSE_CODE_UTF8_MOTION(key[0]) || MOUSE_CODE_UTF8_END(key[0])))
{
return NULL;
}
@@ -301,7 +537,7 @@ gui_mouse_event_code2key (const char *code)
gui_mouse_event_x[gui_mouse_event_index] = x;
gui_mouse_event_y[gui_mouse_event_index] = y;
if (gui_mouse_event_index == 0)
- gui_mouse_event_button = code[0];
+ gui_mouse_event_button = key[0];
if (gui_mouse_event_index == 0)
gui_mouse_event_index = 1;
@@ -309,149 +545,71 @@ gui_mouse_event_code2key (const char *code)
/*
* browse wheel codes, if one code is found, return event name immediately
*/
- for (i = 0; gui_mouse_wheel_codes[i][0]; i++)
+ for (i = 0; gui_mouse_wheel_utf8_codes[i][0]; i++)
{
- if (code[0] == gui_mouse_wheel_codes[i][0][0])
+ if (key[0] == gui_mouse_wheel_utf8_codes[i][0][0])
{
- strcat (key, gui_mouse_wheel_codes[i][1]);
+ strcat (mouse_key, gui_mouse_wheel_utf8_codes[i][1]);
gui_mouse_event_x[1] = gui_mouse_event_x[0];
gui_mouse_event_y[1] = gui_mouse_event_y[0];
- return key;
+ return mouse_key;
}
}
/* add name of button event */
- for (i = 0; gui_mouse_button_codes[i][0]; i++)
+ for (i = 0; gui_mouse_button_utf8_codes[i][0]; i++)
{
- if (gui_mouse_event_button == gui_mouse_button_codes[i][0][0])
+ if (gui_mouse_event_button == gui_mouse_button_utf8_codes[i][0][0])
{
- strcat (key, gui_mouse_button_codes[i][1]);
+ strcat (mouse_key, gui_mouse_button_utf8_codes[i][1]);
break;
}
}
/* nothing found, reset now or mouse will be stuck */
- if (!key[0])
+ if (!mouse_key[0])
{
gui_mouse_event_reset ();
return NULL;
}
- if (!MOUSE_CODE_END(code[0]))
+ if (!MOUSE_CODE_UTF8_END(key[0]))
{
- strcat (key, "-event-");
- if (MOUSE_CODE_MOTION(code[0])) {
- strcat (key, "drag");
+ strcat (mouse_key, "-event-");
+ if (MOUSE_CODE_UTF8_MOTION(key[0])) {
+ strcat (mouse_key, "drag");
}
else
{
gui_mouse_event_x[1] = gui_mouse_event_x[0];
gui_mouse_event_y[1] = gui_mouse_event_y[0];
- strcat (key, "down");
+ strcat (mouse_key, "down");
}
- return key;
+ return mouse_key;
}
- /*
- * Mouse gesture: if (x,y) on release is different from (x,y) on click,
- * compute distance and angle between 2 points.
- *
- * Distance: sqrt((x2-x1)²+(y2-y1)²)
- * Angle : atan2(x1-x1, y2-y1)
- *
- * Angle:
- *
- * 3.14 pi
- * /\
- * -2.35 || 2.35 3/4 * pi
- * ||
- * -1.57 /----++----\ 1.57 1/2 * pi
- * \----++----/
- * ||
- * -0.78 || 0.78 1/4 * pi
- * \/
- * 0.00 0
- *
- * Possible returned gestures are:
- *
- * key name | dist. | angle
- * ---------------------------+-------+--------------------------
- * buttonX-gesture-up | 3..19 | -2.35..-3.14 + 2.35..3.14
- * buttonX-gesture-up-long | >= 20 |
- * buttonX-gesture-down | 3..19 | -0.78..0.78
- * buttonX-gesture-down-long | >= 20 |
- * buttonX-gesture-left | 3..39 | -0.78..-2.35
- * buttonX-gesture-left-long | >= 40 |
- * buttonX-gesture-right | 3..39 | 0.78..2.35
- * buttonX-gesture-right-long | >= 40 |
- */
-
- if (key[0]
- && ((gui_mouse_event_x[0] != gui_mouse_event_x[1])
- || (gui_mouse_event_y[0] != gui_mouse_event_y[1])))
- {
- diff_x = gui_mouse_event_x[1] - gui_mouse_event_x[0];
- diff_y = gui_mouse_event_y[1] - gui_mouse_event_y[0];
- distance = sqrt ((diff_x * diff_x) + (diff_y * diff_y));
- if (distance >= 3)
- {
- angle = atan2 ((double)(gui_mouse_event_x[1] - gui_mouse_event_x[0]),
- (double)(gui_mouse_event_y[1] - gui_mouse_event_y[0]));
- pi4 = 3.14159265358979 / 4;
- if ((angle <= pi4 * (-3)) || (angle >= pi4 * 3))
- {
- strcat (key, "-gesture-up");
- if (distance >= 20)
- strcat (key, "-long");
- }
- else if ((angle >= pi4 * (-1)) && (angle <= pi4))
- {
- strcat (key, "-gesture-down");
- if (distance >= 20)
- strcat (key, "-long");
- }
- else if ((angle >= pi4 * (-3)) && (angle <= pi4 * (-1)))
- {
- strcat (key, "-gesture-left");
- if (distance >= 40)
- strcat (key, "-long");
- }
- else if ((angle >= pi4) && (angle <= pi4 * 3))
- {
- strcat (key, "-gesture-right");
- if (distance >= 40)
- strcat (key, "-long");
- }
- }
- }
+ gui_mouse_event_concat_gesture (mouse_key);
- return key;
+ return mouse_key;
}
/*
- * Ends mouse event.
+ * Processes a mouse event.
*/
void
-gui_mouse_event_end ()
+gui_mouse_event_process (const char *key)
{
const char *mouse_key;
int bare_event;
- if (gui_key_debug)
- gui_key_debug_print_key (gui_key_combo, NULL, NULL, NULL, 1);
+ /* get mouse event name */
+ mouse_key = NULL;
+ if (strncmp (key, "\x01[[<", 4) == 0)
+ mouse_key = gui_mouse_event_name_sgr (key + 4);
+ else if (strncmp (key, "\x01[[M", 4) == 0)
+ mouse_key = gui_mouse_event_name_utf8 (key + 4);
- gui_mouse_event_pending = 0;
-
- /* end mouse event timer */
- if (gui_mouse_event_timer)
- {
- unhook (gui_mouse_event_timer);
- gui_mouse_event_timer = NULL;
- }
-
- /* get key from mouse code */
- mouse_key = gui_mouse_event_code2key (gui_key_combo);
if (mouse_key && mouse_key[0])
{
bare_event = string_match (mouse_key, "*-event-*", 1);
@@ -468,6 +626,4 @@ gui_mouse_event_end ()
if (!bare_event)
gui_mouse_event_reset ();
}
-
- gui_key_combo[0] = '\0';
}
diff --git a/src/gui/curses/gui-curses-mouse.h b/src/gui/curses/gui-curses-mouse.h
index 0fe47d894..35f4201b9 100644
--- a/src/gui/curses/gui-curses-mouse.h
+++ b/src/gui/curses/gui-curses-mouse.h
@@ -20,9 +20,8 @@
#ifndef WEECHAT_GUI_CURSES_MOUSE_H
#define WEECHAT_GUI_CURSES_MOUSE_H
-#define MOUSE_CODE_BUTTON(code) ((code >= 32) && (code < 64))
-#define MOUSE_CODE_MOTION(code) ((code >= 64) && (code < 96))
-#define MOUSE_CODE_END(code) ((code == '#') || (code == '3') \
- || (code == '+') || (code == ';'))
+#define MOUSE_CODE_UTF8_MOTION(code) ((code >= 64) && (code < 96))
+#define MOUSE_CODE_UTF8_END(code) ((code == '#') || (code == '3') \
+ || (code == '+') || (code == ';'))
#endif /* WEECHAT_GUI_CURSES_MOUSE_H */