From 122e1720927646d81f3aaa24abacc2860de93a35 Mon Sep 17 00:00:00 2001 From: Trygve Aaberge Date: Wed, 29 Apr 2020 23:43:42 +0200 Subject: [PATCH 1/3] Track modifier state correctly When a modifier key is pressed, that modifier is not included in mods. When it is released, it is included. Therefore, we have to special case the modifier keys when storing the modifiers that are active. When a modifier key is pressed, we add the modifier to mods_at_last_key_or_button_event, and when it is released, we remove it. This fixes an issue where move and drag events would still be sent to the terminal program after pressing shift, but would stop being sent after releasing shift until another key or button was pressed. --- kitty/glfw.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/kitty/glfw.c b/kitty/glfw.c index 2eb1e659e..a21c9238e 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -227,10 +227,38 @@ refresh_callback(GLFWwindow *w) { static int mods_at_last_key_or_button_event = 0; +static inline int +key_to_modifier(int key) { + switch(key) { + case GLFW_KEY_LEFT_SHIFT: + case GLFW_KEY_RIGHT_SHIFT: + return GLFW_MOD_SHIFT; + case GLFW_KEY_LEFT_CONTROL: + case GLFW_KEY_RIGHT_CONTROL: + return GLFW_MOD_CONTROL; + case GLFW_KEY_LEFT_ALT: + case GLFW_KEY_RIGHT_ALT: + return GLFW_MOD_ALT; + case GLFW_KEY_LEFT_SUPER: + case GLFW_KEY_RIGHT_SUPER: + return GLFW_MOD_SUPER; + default: + return -1; + } +} + static void key_callback(GLFWwindow *w, GLFWkeyevent *ev) { if (!set_callback_window(w)) return; mods_at_last_key_or_button_event = ev->mods; + int key_modifier = key_to_modifier(ev->key); + if (key_modifier != -1) { + if (ev->action == GLFW_RELEASE) { + mods_at_last_key_or_button_event &= ~key_modifier; + } else { + mods_at_last_key_or_button_event |= key_modifier; + } + } global_state.callback_os_window->cursor_blink_zero_time = monotonic(); if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST) { global_state.callback_os_window->is_key_pressed[ev->key] = ev->action == GLFW_RELEASE ? false : true; From c78563b452c3eee265e39456dcd250f4bf500760 Mon Sep 17 00:00:00 2001 From: Trygve Aaberge Date: Thu, 30 Apr 2020 00:14:00 +0200 Subject: [PATCH 2/3] Report modifier key state when sending move and drag events to the terminal program --- kitty/mouse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitty/mouse.c b/kitty/mouse.c index 7453eed95..653fd3735 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -341,7 +341,7 @@ HANDLER(handle_move_event) { handle_mouse_movement_in_kitty(w, button, mouse_cell_changed | cell_half_changed); } else { if (!mouse_cell_changed) return; - int sz = encode_mouse_event(w, MAX(0, button), button >=0 ? DRAG : MOVE, 0); + int sz = encode_mouse_event(w, MAX(0, button), button >=0 ? DRAG : MOVE, modifiers); if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); } } } From 8efe08c45bcaf21dc3ed8e05000a80671816687d Mon Sep 17 00:00:00 2001 From: Trygve Aaberge Date: Thu, 30 Apr 2020 01:40:07 +0200 Subject: [PATCH 3/3] Support more mouse buttons for terminal mouse events Previously, the mouse back and forward buttons sent the same codes as scroll up and down. Now they instead send the same codes as xterm. Mouse button 10 (in X11 numbering) also now sends the same as xterm, instead of not sending anything. This also changes the `send_mouse_event` function which can be called from kittens to use X11 numbering for mouse buttons instead of what it previously used, which turns out to be a hybrid of X11 and GLFW. It was documented to use GLFW numbering, but GLFW doesn't have numbers for scroll events (that's separate events with x/y offsets) and 4 and 5 in GLFW is actually back and forward, while `send_mouse_event` interpreted it as scroll up and down. That means that this is a breaking change for `send_mouse_event` because it swaps the number for the middle and right button to be consistent with X11. I did this because I think it's better to use one consistent numbering scheme for the function, and because people probably know X11 numbering better than GLFW numbering and GLFW doesn't have numbers for the scroll buttons. --- docs/kittens/custom.rst | 13 ++++---- kitty/mouse.c | 68 +++++++++++++++++++++++++---------------- kitty_tests/keys.py | 2 +- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/docs/kittens/custom.rst b/docs/kittens/custom.rst index acdafdf37..a8f847226 100644 --- a/docs/kittens/custom.rst +++ b/docs/kittens/custom.rst @@ -142,12 +142,13 @@ those using:: send_mouse_event(screen, x, y, button, action, mods) ``screen`` is the ``screen`` attribute of the window you want to send the event -to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is -``GLFW_MOUSE_BUTTON_{button}`` where ``{button}`` is one of ``LEFT``, -``RIGHT``, ``MIDDLE`` or a digit from ``1`` to ``8``. ``action`` is one of -``PRESS``, ``RELEASE``, ``DRAG`` or ``MOVE``. ``mods`` is a bitmask of -``GLFW_MOD_{mod}`` where ``{mod}`` is one of ``SHIFT``, ``CONTROL`` or ``ALT``. -All the mentioned constants are imported from ``kitty.fast_data_types``. +to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is a number using +the same numbering as X11 (left: ``1``, middle: ``2``, right: ``3``, scroll up: +``4``, scroll down: ``5``, scroll left: ``6``, scroll right: ``7``, back: +``8``, forward: ``9``). ``action`` is one of ``PRESS``, ``RELEASE``, ``DRAG`` +or ``MOVE``. ``mods`` is a bitmask of ``GLFW_MOD_{mod}`` where ``{mod}`` is one +of ``SHIFT``, ``CONTROL`` or ``ALT``. All the mentioned constants are imported +from ``kitty.fast_data_types``. For example, to send a left click at position x: 2, y: 3 to the active window:: diff --git a/kitty/mouse.c b/kitty/mouse.c index 653fd3735..a89158f84 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -25,27 +25,43 @@ typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction; #define ALT_INDICATOR (1 << 3) #define CONTROL_INDICATOR (1 << 4) #define MOTION_INDICATOR (1 << 5) -#define EXTRA_BUTTON_INDICATOR (1 << 6) +#define SCROLL_BUTTON_INDICATOR (1 << 6) +#define EXTRA_BUTTON_INDICATOR (1 << 7) static inline unsigned int button_map(int button) { switch(button) { case GLFW_MOUSE_BUTTON_LEFT: - return 0; - case GLFW_MOUSE_BUTTON_RIGHT: - return 2; - case GLFW_MOUSE_BUTTON_MIDDLE: return 1; + case GLFW_MOUSE_BUTTON_RIGHT: + return 3; + case GLFW_MOUSE_BUTTON_MIDDLE: + return 2; case GLFW_MOUSE_BUTTON_4: - return EXTRA_BUTTON_INDICATOR; case GLFW_MOUSE_BUTTON_5: - return EXTRA_BUTTON_INDICATOR | 1; + case GLFW_MOUSE_BUTTON_6: + case GLFW_MOUSE_BUTTON_7: + case GLFW_MOUSE_BUTTON_8: + return button + 5; default: return UINT_MAX; } } +static inline unsigned int +encode_button(unsigned int button) { + if (button >= 8 && button <= 11) { + return (button - 8) | EXTRA_BUTTON_INDICATOR; + } else if (button >= 4 && button <= 7) { + return (button - 4) | SCROLL_BUTTON_INDICATOR; + } else if (button >= 1 && button <= 3) { + return button - 1; + } else { + return UINT_MAX; + } +} + static char mouse_event_buf[64]; static inline int @@ -54,7 +70,7 @@ encode_mouse_event_impl(unsigned int x, unsigned int y, int mouse_tracking_proto if (action == MOVE) { cb = 3; } else { - cb = button_map(button); + cb = encode_button(button); if (cb == UINT_MAX) return 0; } if (action == DRAG || action == MOVE) cb |= MOTION_INDICATOR; @@ -92,7 +108,16 @@ encode_mouse_event(Window *w, int button, MouseAction action, int mods) { unsigned int x = w->mouse_pos.cell_x + 1, y = w->mouse_pos.cell_y + 1; // 1 based indexing Screen *screen = w->render_data.screen; return encode_mouse_event_impl(x, y, screen->modes.mouse_tracking_protocol, button, action, mods); +} +static int +encode_mouse_button(Window *w, int button, MouseAction action, int mods) { + return encode_mouse_event(w, button_map(button), action, mods); +} + +static int +encode_mouse_scroll(Window *w, bool upwards, int mods) { + return encode_mouse_event(w, upwards ? 4 : 5, PRESS, mods); } // }}} @@ -341,7 +366,7 @@ HANDLER(handle_move_event) { handle_mouse_movement_in_kitty(w, button, mouse_cell_changed | cell_half_changed); } else { if (!mouse_cell_changed) return; - int sz = encode_mouse_event(w, MAX(0, button), button >=0 ? DRAG : MOVE, modifiers); + int sz = encode_mouse_button(w, MAX(0, button), button >=0 ? DRAG : MOVE, modifiers); if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); } } } @@ -450,34 +475,25 @@ HANDLER(handle_button_event) { ); if (handle_in_kitty) handle_button_event_in_kitty(w, button, modifiers, is_release); else { - int sz = encode_mouse_event(w, button, is_release ? RELEASE : PRESS, modifiers); + int sz = encode_mouse_button(w, button, is_release ? RELEASE : PRESS, modifiers); if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); } } } static inline int currently_pressed_button(void) { - for (int i = 0; i < GLFW_MOUSE_BUTTON_5; i++) { + for (int i = 0; i <= GLFW_MOUSE_BUTTON_8; i++) { if (global_state.callback_os_window->mouse_button_pressed[i]) return i; } return -1; } HANDLER(handle_event) { - switch(button) { - case -1: - button = currently_pressed_button(); - handle_move_event(w, button, modifiers, window_idx); - break; - case GLFW_MOUSE_BUTTON_LEFT: - case GLFW_MOUSE_BUTTON_RIGHT: - case GLFW_MOUSE_BUTTON_MIDDLE: - case GLFW_MOUSE_BUTTON_4: - case GLFW_MOUSE_BUTTON_5: - handle_button_event(w, button, modifiers, window_idx); - break; - default: - break; + if (button == -1) { + button = currently_pressed_button(); + handle_move_event(w, button, modifiers, window_idx); + } else { + handle_button_event(w, button, modifiers, window_idx); } } @@ -690,7 +706,7 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags, int modifiers) { screen_history_scroll(screen, abs(s), upwards); } else { if (screen->modes.mouse_tracking_mode) { - int sz = encode_mouse_event(w, upwards ? GLFW_MOUSE_BUTTON_4 : GLFW_MOUSE_BUTTON_5, PRESS, modifiers); + int sz = encode_mouse_scroll(w, upwards, modifiers); if (sz > 0) { mouse_event_buf[sz] = 0; for (s = abs(s); s > 0; s--) { diff --git a/kitty_tests/keys.py b/kitty_tests/keys.py index a3d6ff34c..a4eacdeda 100644 --- a/kitty_tests/keys.py +++ b/kitty_tests/keys.py @@ -81,7 +81,7 @@ class TestParser(BaseTest): def test_encode_mouse_event(self): NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4) - L, M, R = defines.GLFW_MOUSE_BUTTON_LEFT, defines.GLFW_MOUSE_BUTTON_MIDDLE, defines.GLFW_MOUSE_BUTTON_RIGHT + L, M, R = 1, 2, 3 protocol = SGR_PROTOCOL def enc(button=L, action=defines.PRESS, mods=0, x=1, y=1):