Add support for sending mouse events from a kitten

This allows you to a control a program running in kitty from a kitten
using mouse events. If the program is not receiving mouse events of that
type, it is not sent.
This commit is contained in:
Trygve Aaberge 2020-04-12 15:17:19 +02:00
parent b4d08044a0
commit 43af6e3b8a
4 changed files with 64 additions and 9 deletions

View File

@ -132,6 +132,32 @@ layout, by simply adding the line::
To the ``handle_result()`` function, above.
Sending mouse events
--------------------
If the program running in a window is receiving mouse events you can simulate
those using::
from kitty.fast_data_types import send_mouse_event
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``.
For example, to send a left click at position x: 2, y: 3 to the active window::
from kitty.fast_data_types import send_mouse_event, GLFW_MOUSE_BUTTON_LEFT, PRESS
send_mouse_event(boss.active_window.screen, 2, 3, GLFW_MOUSE_BUTTON_LEFT, PRESS, 0)
The function will only send the event if the program is receiving events of
that type, and will return ``True`` if it sent the event, and ``False`` if not.
Debugging kittens
--------------------

View File

@ -327,6 +327,10 @@ FC_WIDTH_NORMAL: int
FC_SLANT_ROMAN: int
FC_SLANT_ITALIC: int
BORDERS_PROGRAM: int
PRESS: int
RELEASE: int
DRAG: int
MOVE: int
# }}}

View File

@ -15,6 +15,8 @@
#include "control-codes.h"
#include "monotonic.h"
extern PyTypeObject Screen_Type;
static MouseShape mouse_cursor_shape = BEAM;
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
@ -698,6 +700,25 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
}
}
static PyObject*
send_mouse_event(PyObject *self UNUSED, PyObject *args) {
Screen *screen;
unsigned int x, y;
int button, action, mods;
if (!PyArg_ParseTuple(args, "O!IIiii", &Screen_Type, &screen, &x, &y, &button, &action, &mods)) return NULL;
MouseTrackingMode mode = screen->modes.mouse_tracking_mode;
if (mode == ANY_MODE || (mode == MOTION_MODE && action != MOVE) || (mode == BUTTON_MODE && (action == PRESS || action == RELEASE))) {
int sz = encode_mouse_event_impl(x + 1, y + 1, screen->modes.mouse_tracking_protocol, button, action, mods);
if (sz > 0) {
mouse_event_buf[sz] = 0;
write_escape_code_to_child(screen, CSI, mouse_event_buf);
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
static PyObject*
test_encode_mouse(PyObject *self UNUSED, PyObject *args) {
unsigned int x, y;
@ -732,6 +753,7 @@ send_mock_mouse_event_to_window(PyObject *self UNUSED, PyObject *args) {
}
static PyMethodDef module_methods[] = {
METHODB(send_mouse_event, METH_VARARGS),
METHODB(test_encode_mouse, METH_VARARGS),
METHODB(send_mock_mouse_event_to_window, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
@ -739,6 +761,10 @@ static PyMethodDef module_methods[] = {
bool
init_mouse(PyObject *module) {
PyModule_AddIntMacro(module, PRESS);
PyModule_AddIntMacro(module, RELEASE);
PyModule_AddIntMacro(module, DRAG);
PyModule_AddIntMacro(module, MOVE);
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
return true;
}

View File

@ -80,26 +80,25 @@ class TestParser(BaseTest):
km(modify_key_bytes(base_key, num).decode('ascii')[1:], key)
def test_encode_mouse_event(self):
PRESS, RELEASE, DRAG, MOVE = 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
protocol = SGR_PROTOCOL
def enc(button=L, action=PRESS, mods=0, x=1, y=1):
def enc(button=L, action=defines.PRESS, mods=0, x=1, y=1):
return defines.test_encode_mouse(x, y, protocol, button, action, mods)
self.ae(enc(), '<0;1;1M')
self.ae(enc(action=RELEASE), '<0;1;1m')
self.ae(enc(action=MOVE), '<35;1;1M')
self.ae(enc(action=DRAG), '<32;1;1M')
self.ae(enc(action=defines.RELEASE), '<0;1;1m')
self.ae(enc(action=defines.MOVE), '<35;1;1M')
self.ae(enc(action=defines.DRAG), '<32;1;1M')
self.ae(enc(R), '<2;1;1M')
self.ae(enc(R, action=RELEASE), '<2;1;1m')
self.ae(enc(R, action=DRAG), '<34;1;1M')
self.ae(enc(R, action=defines.RELEASE), '<2;1;1m')
self.ae(enc(R, action=defines.DRAG), '<34;1;1M')
self.ae(enc(M), '<1;1;1M')
self.ae(enc(M, action=RELEASE), '<1;1;1m')
self.ae(enc(M, action=DRAG), '<33;1;1M')
self.ae(enc(M, action=defines.RELEASE), '<1;1;1m')
self.ae(enc(M, action=defines.DRAG), '<33;1;1M')
self.ae(enc(x=1234, y=5678), '<0;1234;5678M')
self.ae(enc(mods=defines.GLFW_MOD_SHIFT), '<4;1;1M')