diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 59261ab79..f2024f518 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1343,5 +1343,5 @@ def set_os_window_title(os_window_id: int, title: str) -> None: pass -def update_ime_position_for_window(window_id: int, force: bool = False) -> bool: +def update_ime_position_for_window(window_id: int, force: bool = False, lost_focus: bool = False) -> bool: pass diff --git a/kitty/keys.c b/kitty/keys.c index 5985182d9..3df22607d 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -79,6 +79,12 @@ active_window(void) { return NULL; } +void +update_ime_focus(OSWindow *osw, bool focused) { + GLFWIMEUpdateEvent ev = { .focused = focused, .type = GLFW_IME_UPDATE_FOCUS }; + glfwUpdateIMEState(osw->handle, &ev); +} + void update_ime_position(Window* w, Screen *screen) { unsigned int cell_width = global_state.callback_os_window->fonts_data->cell_width, cell_height = global_state.callback_os_window->fonts_data->cell_height; diff --git a/kitty/screen.c b/kitty/screen.c index a3211a41a..baa698571 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -762,7 +762,7 @@ restore_overlay_line(struct SaveOverlayLine *sol) { debug("Received input from child (%s) while overlay active. Overlay contents: %s\n", sol->func_name, PyUnicode_AsUTF8(sol->overlay_text)); screen_draw_overlay_text(sol->screen, PyUnicode_AsUTF8(sol->overlay_text)); Py_DECREF(sol->overlay_text); - update_ime_position_for_window(sol->screen->window_id, false); + update_ime_position_for_window(sol->screen->window_id, false, false); } } diff --git a/kitty/state.c b/kitty/state.c index c221d5d2d..e32522967 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -602,7 +602,7 @@ send_pending_click_to_window_id(id_type timer_id UNUSED, void *data) { } bool -update_ime_position_for_window(id_type window_id, bool force) { +update_ime_position_for_window(id_type window_id, bool force, bool lost_focus) { for (size_t o = 0; o < global_state.num_os_windows; o++) { OSWindow *osw = global_state.os_windows + o; for (size_t t = 0; t < osw->num_tabs; t++) { @@ -613,7 +613,8 @@ update_ime_position_for_window(id_type window_id, bool force) { if (window->render_data.screen && (force || osw->is_focused)) { OSWindow *orig = global_state.callback_os_window; global_state.callback_os_window = osw; - update_ime_position(window, window->render_data.screen); + if (lost_focus) update_ime_focus(osw, false); + else update_ime_position(window, window->render_data.screen); global_state.callback_os_window = orig; return true; } @@ -652,8 +653,9 @@ update_ime_position_for_window(id_type window_id, bool force) { PYWRAP1(update_ime_position_for_window) { id_type window_id; int force = 0; - PA("K|p", &window_id, &force); - if (update_ime_position_for_window(window_id, force)) Py_RETURN_TRUE; + int lost_focus = 0; + PA("K|pp", &window_id, &force, &lost_focus); + if (update_ime_position_for_window(window_id, force, lost_focus)) Py_RETURN_TRUE; Py_RETURN_FALSE; } diff --git a/kitty/state.h b/kitty/state.h index 1631ed502..903601f73 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -335,6 +335,7 @@ void get_platform_dependent_config_values(void *glfw_window); bool draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height); uint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height); bool is_os_window_fullscreen(OSWindow *); +void update_ime_focus(OSWindow* osw, bool focused); void update_ime_position(Window* w, Screen *screen); -bool update_ime_position_for_window(id_type window_id, bool force); +bool update_ime_position_for_window(id_type window_id, bool force, bool lost_focus); void set_ignore_os_keyboard_processing(bool enabled); diff --git a/kitty/window.py b/kitty/window.py index c11ae0408..c5d470384 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -689,6 +689,9 @@ class Window: tab = self.tabref() if tab is not None: tab.relayout_borders() + else: + # Cancel IME composition after loses focus + update_ime_position_for_window(self.id, True, True) def title_changed(self, new_title: Optional[str]) -> None: self.child_title = sanitize_title(new_title or self.default_title) @@ -1021,6 +1024,8 @@ class Window: self.call_watchers(self.watchers.on_close, {}) self.destroyed = True if hasattr(self, 'screen'): + # Cancel IME composition when window is destroyed + update_ime_position_for_window(self.id, True, True) # Remove cycles so that screen is de-allocated immediately self.screen.reset_callbacks() del self.screen