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/glfw.c b/kitty/glfw.c index 87d605d6f..f4055dd3d 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; diff --git a/kitty/mouse.c b/kitty/mouse.c index 099dbcb76..191b355fe 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, 0); + 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):