Add click and doubleclick events as well

This commit is contained in:
Kovid Goyal 2021-05-11 13:59:52 +05:30
parent 1c9674cec9
commit cb21422836
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 51 additions and 11 deletions

View File

@ -492,7 +492,7 @@ def parse_mouse_action(val: str, mouse_mappings: List[MouseMapping]) -> None:
log_error(f'Mouse button: {xbutton} not recognized, ignoring') log_error(f'Mouse button: {xbutton} not recognized, ignoring')
return return
try: try:
count = {'release': -1, 'press': 1, 'doublepress': 2, 'triplepress': 3}[event.lower()] count = {'doubleclick': -3, 'click': -2, 'release': -1, 'press': 1, 'doublepress': 2, 'triplepress': 3}[event.lower()]
except KeyError: except KeyError:
log_error(f'Mouse event type: {event} not recognized, ignoring') log_error(f'Mouse event type: {event} not recognized, ignoring')
return return

View File

@ -154,16 +154,17 @@ with added keyboard modifiers, for example: ``ctrl+shift+left`` refers to holdin
the :kbd:`ctrl+shift` keys while clicking with the left mouse button. The the :kbd:`ctrl+shift` keys while clicking with the left mouse button. The
number ``b1 ... b8`` can be used to refer to upto eight buttons on a mouse. number ``b1 ... b8`` can be used to refer to upto eight buttons on a mouse.
``event-type`` is one ``press``, ``release``, ``doublepress`` and ``triplepress``. ``event-type`` is one ``press``, ``release``, ``doublepress``, ``triplepress``,
``modes`` indicates whether the action is performed when the mouse is grabbed by the ``click`` and ``doubleclick``. ``modes`` indicates whether the action is
terminal application or not. It can have one or more or the values, ``grabbed,ungrabbed``. performed when the mouse is grabbed by the terminal application or not. It can
have one or more or the values, ``grabbed,ungrabbed``.
You can run kitty with the :option:`kitty --debug-input` command line option You can run kitty with the :option:`kitty --debug-input` command line option
to see mouse events. See the builtin actions below to get a sense of what is possible. to see mouse events. See the builtin actions below to get a sense of what is possible.
.. note:: .. note::
Once a selection drag is started, releasing the button that started it will Once a selection is started, releasing the button that started it will
automatically end it. automatically end it and no release event will be dispatched.
'''), '''),
], ],

View File

@ -127,6 +127,29 @@ static bool
dispatch_mouse_event(Window *w, int button, int count, int modifiers, bool grabbed) { dispatch_mouse_event(Window *w, int button, int count, int modifiers, bool grabbed) {
bool handled = false; bool handled = false;
if (w->render_data.screen && w->render_data.screen->callbacks != Py_None) { if (w->render_data.screen && w->render_data.screen->callbacks != Py_None) {
if (OPT(debug_keyboard)) {
const char *evname = "move";
switch(count) {
case -3: evname = "doubleclick"; break;
case -2: evname = "click"; break;
case -1: evname = "release"; break;
case 1: evname = "press"; break;
case 2: evname = "doublepress"; break;
case 3: evname = "triplepress"; break;
}
const char *bname = "unknown";
switch(button) {
case GLFW_MOUSE_BUTTON_LEFT: bname = "left"; break;
case GLFW_MOUSE_BUTTON_MIDDLE: bname = "middle"; break;
case GLFW_MOUSE_BUTTON_RIGHT: bname = "right"; break;
case GLFW_MOUSE_BUTTON_4: bname = "b4"; break;
case GLFW_MOUSE_BUTTON_5: bname = "b5"; break;
case GLFW_MOUSE_BUTTON_6: bname = "b6"; break;
case GLFW_MOUSE_BUTTON_7: bname = "b7"; break;
case GLFW_MOUSE_BUTTON_8: bname = "b8"; break;
}
debug("\x1b[33mon_mouse_input\x1b[m: %s button: %s %sgrabbed: %d\n", evname, bname, format_mods(modifiers), grabbed);
}
PyObject *callback_ret = PyObject_CallMethod(w->render_data.screen->callbacks, "on_mouse_event", "{si si si sO}", PyObject *callback_ret = PyObject_CallMethod(w->render_data.screen->callbacks, "on_mouse_event", "{si si si sO}",
"button", button, "repeat_count", count, "mods", modifiers, "grabbed", grabbed ? Py_True : Py_False); "button", button, "repeat_count", count, "mods", modifiers, "grabbed", grabbed ? Py_True : Py_False);
if (callback_ret == NULL) PyErr_Print(); if (callback_ret == NULL) PyErr_Print();
@ -402,6 +425,14 @@ clear_click_queue(Window *w, int button) {
#define N(n) (q->clicks[q->length - n]) #define N(n) (q->clicks[q->length - n])
static bool
release_is_click(Window *w, int button) {
ClickQueue *q = &w->click_queues[button];
double click_allowed_radius = 1.2 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->cell_height : 20);
monotonic_t now = monotonic();
return (q->length > 0 && distance(N(1).x, N(1).y, w->mouse_pos.x, w->mouse_pos.y) <= click_allowed_radius && now - N(1).at < OPT(click_interval));
}
static unsigned static unsigned
multi_click_count(Window *w, int button) { multi_click_count(Window *w, int button) {
ClickQueue *q = &w->click_queues[button]; ClickQueue *q = &w->click_queues[button];
@ -424,7 +455,8 @@ multi_click_count(Window *w, int button) {
} }
HANDLER(add_click) { static void
add_press(Window *w, int button, int modifiers) {
if (button < 0 || button > (ssize_t)arraysz(w->click_queues)) return; if (button < 0 || button > (ssize_t)arraysz(w->click_queues)) return;
modifiers &= ~GLFW_LOCK_MASK; modifiers &= ~GLFW_LOCK_MASK;
ClickQueue *q = &w->click_queues[button]; ClickQueue *q = &w->click_queues[button];
@ -448,6 +480,13 @@ mouse_open_url(Window *w) {
screen_open_url(screen); screen_open_url(screen);
} }
static void
dispatch_possible_click(Window *w, int button, int modifiers) {
Screen *screen = w->render_data.screen;
int count = multi_click_count(w, button);
if (release_is_click(w, button)) dispatch_mouse_event(w, button, count == 2 ? -3 : -2, modifiers, screen->modes.mouse_tracking_mode != 0);
}
HANDLER(handle_button_event) { HANDLER(handle_button_event) {
modifiers &= ~GLFW_LOCK_MASK; modifiers &= ~GLFW_LOCK_MASK;
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab; Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
@ -463,9 +502,8 @@ HANDLER(handle_button_event) {
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); } if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); }
} }
} }
if (!is_release) add_click(w, button, modifiers, 0); if (is_release) dispatch_possible_click(w, button, modifiers);
else { else add_press(w, button, modifiers);
}
} }
static inline int static inline int
@ -658,6 +696,7 @@ mouse_event(int button, int modifiers, int action) {
if (w) { if (w) {
end_drag(w); end_drag(w);
debug("handled as drag end\n"); debug("handled as drag end\n");
dispatch_possible_click(w, button, modifiers);
return; return;
} }
} }
@ -836,7 +875,7 @@ send_mock_mouse_event_to_window(PyObject *self UNUSED, PyObject *args) {
dispatch_mouse_event(w, button, is_release ? -1 : 1, modifiers, false); dispatch_mouse_event(w, button, is_release ? -1 : 1, modifiers, false);
if (!is_release) { if (!is_release) {
last_button_pressed = button; last_button_pressed = button;
add_click(w, button, modifiers, 0); add_press(w, button, modifiers);
} }
} }
} }