mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
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.
This commit is contained in:
@@ -142,12 +142,13 @@ those using::
|
|||||||
send_mouse_event(screen, x, y, button, action, mods)
|
send_mouse_event(screen, x, y, button, action, mods)
|
||||||
|
|
||||||
``screen`` is the ``screen`` attribute of the window you want to send the event
|
``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
|
to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is a number using
|
||||||
``GLFW_MOUSE_BUTTON_{button}`` where ``{button}`` is one of ``LEFT``,
|
the same numbering as X11 (left: ``1``, middle: ``2``, right: ``3``, scroll up:
|
||||||
``RIGHT``, ``MIDDLE`` or a digit from ``1`` to ``8``. ``action`` is one of
|
``4``, scroll down: ``5``, scroll left: ``6``, scroll right: ``7``, back:
|
||||||
``PRESS``, ``RELEASE``, ``DRAG`` or ``MOVE``. ``mods`` is a bitmask of
|
``8``, forward: ``9``). ``action`` is one of ``PRESS``, ``RELEASE``, ``DRAG``
|
||||||
``GLFW_MOD_{mod}`` where ``{mod}`` is one of ``SHIFT``, ``CONTROL`` or ``ALT``.
|
or ``MOVE``. ``mods`` is a bitmask of ``GLFW_MOD_{mod}`` where ``{mod}`` is one
|
||||||
All the mentioned constants are imported from ``kitty.fast_data_types``.
|
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::
|
For example, to send a left click at position x: 2, y: 3 to the active window::
|
||||||
|
|
||||||
|
|||||||
@@ -25,27 +25,43 @@ typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
|
|||||||
#define ALT_INDICATOR (1 << 3)
|
#define ALT_INDICATOR (1 << 3)
|
||||||
#define CONTROL_INDICATOR (1 << 4)
|
#define CONTROL_INDICATOR (1 << 4)
|
||||||
#define MOTION_INDICATOR (1 << 5)
|
#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
|
static inline unsigned int
|
||||||
button_map(int button) {
|
button_map(int button) {
|
||||||
switch(button) {
|
switch(button) {
|
||||||
case GLFW_MOUSE_BUTTON_LEFT:
|
case GLFW_MOUSE_BUTTON_LEFT:
|
||||||
return 0;
|
|
||||||
case GLFW_MOUSE_BUTTON_RIGHT:
|
|
||||||
return 2;
|
|
||||||
case GLFW_MOUSE_BUTTON_MIDDLE:
|
|
||||||
return 1;
|
return 1;
|
||||||
|
case GLFW_MOUSE_BUTTON_RIGHT:
|
||||||
|
return 3;
|
||||||
|
case GLFW_MOUSE_BUTTON_MIDDLE:
|
||||||
|
return 2;
|
||||||
case GLFW_MOUSE_BUTTON_4:
|
case GLFW_MOUSE_BUTTON_4:
|
||||||
return EXTRA_BUTTON_INDICATOR;
|
|
||||||
case GLFW_MOUSE_BUTTON_5:
|
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:
|
default:
|
||||||
return UINT_MAX;
|
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 char mouse_event_buf[64];
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
@@ -54,7 +70,7 @@ encode_mouse_event_impl(unsigned int x, unsigned int y, int mouse_tracking_proto
|
|||||||
if (action == MOVE) {
|
if (action == MOVE) {
|
||||||
cb = 3;
|
cb = 3;
|
||||||
} else {
|
} else {
|
||||||
cb = button_map(button);
|
cb = encode_button(button);
|
||||||
if (cb == UINT_MAX) return 0;
|
if (cb == UINT_MAX) return 0;
|
||||||
}
|
}
|
||||||
if (action == DRAG || action == MOVE) cb |= MOTION_INDICATOR;
|
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
|
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;
|
Screen *screen = w->render_data.screen;
|
||||||
return encode_mouse_event_impl(x, y, screen->modes.mouse_tracking_protocol, button, action, mods);
|
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);
|
handle_mouse_movement_in_kitty(w, button, mouse_cell_changed | cell_half_changed);
|
||||||
} else {
|
} else {
|
||||||
if (!mouse_cell_changed) return;
|
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); }
|
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);
|
if (handle_in_kitty) handle_button_event_in_kitty(w, button, modifiers, is_release);
|
||||||
else {
|
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); }
|
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
currently_pressed_button(void) {
|
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;
|
if (global_state.callback_os_window->mouse_button_pressed[i]) return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLER(handle_event) {
|
HANDLER(handle_event) {
|
||||||
switch(button) {
|
if (button == -1) {
|
||||||
case -1:
|
button = currently_pressed_button();
|
||||||
button = currently_pressed_button();
|
handle_move_event(w, button, modifiers, window_idx);
|
||||||
handle_move_event(w, button, modifiers, window_idx);
|
} else {
|
||||||
break;
|
handle_button_event(w, button, modifiers, window_idx);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,7 +706,7 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags, int modifiers) {
|
|||||||
screen_history_scroll(screen, abs(s), upwards);
|
screen_history_scroll(screen, abs(s), upwards);
|
||||||
} else {
|
} else {
|
||||||
if (screen->modes.mouse_tracking_mode) {
|
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) {
|
if (sz > 0) {
|
||||||
mouse_event_buf[sz] = 0;
|
mouse_event_buf[sz] = 0;
|
||||||
for (s = abs(s); s > 0; s--) {
|
for (s = abs(s); s > 0; s--) {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class TestParser(BaseTest):
|
|||||||
|
|
||||||
def test_encode_mouse_event(self):
|
def test_encode_mouse_event(self):
|
||||||
NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4)
|
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
|
protocol = SGR_PROTOCOL
|
||||||
|
|
||||||
def enc(button=L, action=defines.PRESS, mods=0, x=1, y=1):
|
def enc(button=L, action=defines.PRESS, mods=0, x=1, y=1):
|
||||||
|
|||||||
Reference in New Issue
Block a user