diff --git a/docs/changelog.rst b/docs/changelog.rst index a75930980..675f8a39b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -144,6 +144,8 @@ To update |kitty|, :doc:`follow the instructions `. - Wayland: Fix key repeat being stopped by the release of an unrelated key (:iss:`2191`) +- Wayland: Add support for the text input protocol (:iss:`3410`) + - Add an option, :opt:`detect_urls` to control whether kitty will detect URLs when the mouse moves over them (:pull:`3118`) diff --git a/glfw/wl_text_input.c b/glfw/wl_text_input.c index 51dfd39e3..e8a6c1673 100644 --- a/glfw/wl_text_input.c +++ b/glfw/wl_text_input.c @@ -13,50 +13,73 @@ static struct zwp_text_input_v3* text_input; static struct zwp_text_input_manager_v3* text_input_manager; +static void commit(void) { if (text_input) zwp_text_input_v3_commit (text_input); } + static void text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { debug("text-input: enter event\n"); + if (text_input) { + zwp_text_input_v3_enable(text_input); + zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); + commit(); + } } static void text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { debug("text-input: leave event\n"); + if (text_input) { + zwp_text_input_v3_disable(text_input); + commit(); + } +} + +static inline void +send_text(const char *text, GLFWIMEState ime_state) { + _GLFWwindow *w = _glfwFocusedWindow(); + if (w && w->callbacks.keyboard) { + GLFWkeyevent fake_ev = {.action = GLFW_PRESS}; + fake_ev.text = text; + fake_ev.ime_state = ime_state; + w->callbacks.keyboard((GLFWwindow*) w, &fake_ev); + } } static void text_input_preedit_string( void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, - const char *text UNUSED, - int32_t cursor_begin UNUSED, - int32_t cursor_end UNUSED + const char *text, + int32_t cursor_begin, + int32_t cursor_end ) { + debug("text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\n", text, cursor_begin, cursor_end); + send_text(text, GLFW_IME_PREEDIT_CHANGED); } static void -text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text UNUSED) { +text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text) { + debug("text-input: commit_string event: text: %s\n", text); + send_text(text, GLFW_IME_COMMIT_TEXT); } static void text_input_delete_surrounding_text( void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, - uint32_t before_length UNUSED, - uint32_t after_length UNUSED) { + uint32_t before_length, + uint32_t after_length) { + debug("text-input: delete_surrounding_text event: before_length: %u after_length: %u\n", before_length, after_length); } static void text_input_done(void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, uint32_t serial UNUSED) { + debug("text-input: done event: serial: %u\n", serial); } void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) { - if (!text_input_manager) { - text_input_manager = - wl_registry_bind(registry, name, - &zwp_text_input_manager_v3_interface, - 1); - } + if (!text_input_manager) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); } void @@ -84,3 +107,23 @@ _glfwWaylandDestroyTextInput(void) { if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager); text_input = NULL; text_input_manager = NULL; } + +void +_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { + if (!text_input) return; + switch(ev->type) { + case GLFW_IME_UPDATE_FOCUS: + debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused); + if (ev->focused) zwp_text_input_v3_enable(text_input); else zwp_text_input_v3_disable(text_input); + commit(); + break; + case GLFW_IME_UPDATE_CURSOR_POSITION: { + const int scale = w->wl.scale; + const int left = ev->cursor.left / scale, top = ev->cursor.top / scale, width = ev->cursor.width / scale, height = ev->cursor.height / scale; + debug("\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\n", left, top, width, height); + zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height); + commit(); + } + break; + } +} diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 4c7bf5dc5..dffcb4beb 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2104,11 +2104,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, return err; } -void -_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { - glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, ev); -} - static void frame_handle_redraw(void *data, struct wl_callback *callback, uint32_t time UNUSED) { _GLFWwindow* window = (_GLFWwindow*) data;