From 22f077b384398962d801fcb8aa141ac0c5534d00 Mon Sep 17 00:00:00 2001 From: pagedown Date: Thu, 27 Jan 2022 09:53:40 +0800 Subject: [PATCH 1/3] Clear overlay text after screen loses focus --- kitty/screen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kitty/screen.c b/kitty/screen.c index 7d73077c8..a3211a41a 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -3651,6 +3651,7 @@ focus_changed(Screen *self, PyObject *has_focus_) { if (has_focus != previous) { self->has_focus = has_focus; if (has_focus) self->has_activity_since_last_focus = false; + else if (self->overlay_line.is_active) deactivate_overlay_line(self); if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, CSI, has_focus ? "I" : "O"); Py_RETURN_TRUE; } From 4d920b182fa89456fe9731f1a050e4e73b68cf7d Mon Sep 17 00:00:00 2001 From: pagedown Date: Thu, 27 Jan 2022 10:07:18 +0800 Subject: [PATCH 2/3] macOS: Cancel IME composition when receive loss of focus event --- glfw/cocoa_window.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index b345b1122..c2091b7a3 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1408,15 +1408,23 @@ is_ascii_control_char(char x) { } void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { - [w->ns.view updateIMEStateFor: ev->type left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height]; + [w->ns.view updateIMEStateFor: ev->type focused:(bool)ev->focused left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height]; } - (void)updateIMEStateFor:(GLFWIMEUpdateType)which + focused:(bool)focused left:(CGFloat)left top:(CGFloat)top cellWidth:(CGFloat)cellWidth cellHeight:(CGFloat)cellHeight { + if (which == GLFW_IME_UPDATE_FOCUS && !focused && [self hasMarkedText] && window) { + [input_context discardMarkedText]; + [self unmarkText]; + GLFWkeyevent glfw_keyevent = {.ime_state = GLFW_IME_PREEDIT_CHANGED}; + _glfwInputKeyboard(window, &glfw_keyevent); + _glfw.ns.text[0] = 0; + } if (which != GLFW_IME_UPDATE_CURSOR_POSITION) return; left /= window->ns.xscale; top /= window->ns.yscale; From a987fb81a0dd9ec2a4449d196a51d04e5a8162f3 Mon Sep 17 00:00:00 2001 From: pagedown Date: Thu, 27 Jan 2022 10:13:57 +0800 Subject: [PATCH 3/3] Emits an IME lost focus event when the window lost focus or closed --- kitty/fast_data_types.pyi | 2 +- kitty/keys.c | 6 ++++++ kitty/screen.c | 2 +- kitty/state.c | 10 ++++++---- kitty/state.h | 3 ++- kitty/window.py | 5 +++++ 6 files changed, 21 insertions(+), 7 deletions(-) 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