From 9a598237c6f654f59691d9277971cdb98dd01085 Mon Sep 17 00:00:00 2001 From: pagedown Date: Wed, 22 Feb 2023 22:36:20 +0800 Subject: [PATCH] macOS: Allow IME to actively get the cursor position in real time IME will automatically get the display position when needed, which keeps it consistent with the overlay as much as possible. Fix the issue that when IME is activated after mouse click, it is displayed at the wrong position. --- glfw/cocoa_window.m | 32 +++++++++++++++++--------------- glfw/glfw3.h | 2 ++ glfw/init.c | 7 +++++++ glfw/internal.h | 1 + kitty/glfw-wrapper.c | 3 +++ kitty/glfw-wrapper.h | 5 +++++ kitty/glfw.c | 21 +++++++++++++++++++++ kitty/keys.c | 13 ++++++++++--- 8 files changed, 66 insertions(+), 18 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 9b66956d4..bd0463589 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1454,15 +1454,11 @@ is_ascii_control_char(char x) { } void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { - [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]; + [w->ns.view updateIMEStateFor: ev->type focused:(bool)ev->focused]; } - (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]; @@ -1472,16 +1468,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { _glfw.ns.text[0] = 0; } if (which != GLFW_IME_UPDATE_CURSOR_POSITION) return; - left /= window->ns.xscale; - top /= window->ns.yscale; - cellWidth /= window->ns.xscale; - cellHeight /= window->ns.yscale; - debug_key("updateIMEPosition: left=%f, top=%f, width=%f, height=%f\n", left, top, cellWidth, cellHeight); - const NSRect frame = [window->ns.view frame]; - const NSRect rectInView = NSMakeRect(left, - frame.size.height - top - cellHeight, - cellWidth, cellHeight); - markedRect = [window->ns.object convertRectToScreen: rectInView]; + if (_glfwPlatformWindowFocused(window)) [[window->ns.view inputContext] invalidateCharacterCoordinates]; } @@ -1507,6 +1494,21 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { actualRange:(NSRangePointer)actualRange { (void)range; (void)actualRange; + if (_glfw.callbacks.get_ime_cursor_position) { + GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION }; + if (_glfw.callbacks.get_ime_cursor_position((GLFWwindow*)window, &ev)) { + const CGFloat left = (CGFloat)ev.cursor.left / window->ns.xscale; + const CGFloat top = (CGFloat)ev.cursor.top / window->ns.yscale; + const CGFloat cellWidth = (CGFloat)ev.cursor.width / window->ns.xscale; + const CGFloat cellHeight = (CGFloat)ev.cursor.height / window->ns.yscale; + debug_key("updateIMEPosition: left=%f, top=%f, width=%f, height=%f\n", left, top, cellWidth, cellHeight); + const NSRect frame = [window->ns.view frame]; + const NSRect rectInView = NSMakeRect(left, + frame.size.height - top - cellHeight, + cellWidth, cellHeight); + markedRect = [window->ns.object convertRectToScreen: rectInView]; + } + } return markedRect; } diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 3d02e5448..35507f9b3 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1732,6 +1732,7 @@ typedef enum { } GLFWClipboardType; typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype); typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz); +typedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev); /*! @brief Video mode type. * @@ -1891,6 +1892,7 @@ GLFWAPI void glfwRemoveTimer(unsigned long long); GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function); GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback); GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback); +GLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun callback); /*! @brief Terminates the GLFW library. * diff --git a/glfw/init.c b/glfw/init.c index d9fd00b75..d9fff1ae1 100644 --- a/glfw/init.c +++ b/glfw/init.c @@ -402,3 +402,10 @@ GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascur _GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun); return cbfun; } + +GLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.get_ime_cursor_position, cbfun); + return cbfun; +} diff --git a/glfw/internal.h b/glfw/internal.h index fe4125a5f..f4044f6e7 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -635,6 +635,7 @@ struct _GLFWlibrary GLFWdrawtextfun draw_text; GLFWcurrentselectionfun get_current_selection; GLFWhascurrentselectionfun has_current_selection; + GLFWimecursorpositionfun get_ime_cursor_position; } callbacks; diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 6ec499bd1..784615412 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -44,6 +44,9 @@ load_glfw(const char* path) { *(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback"); if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror()); + *(void **) (&glfwSetIMECursorPositionCallback_impl) = dlsym(handle, "glfwSetIMECursorPositionCallback"); + if (glfwSetIMECursorPositionCallback_impl == NULL) fail("Failed to load glfw function glfwSetIMECursorPositionCallback with error: %s", dlerror()); + *(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate"); if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror()); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index d847480b2..501220d9b 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1470,6 +1470,7 @@ typedef enum { } GLFWClipboardType; typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype); typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz); +typedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev); /*! @brief Video mode type. * @@ -1667,6 +1668,10 @@ typedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GL GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl; #define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl +typedef GLFWimecursorpositionfun (*glfwSetIMECursorPositionCallback_func)(GLFWimecursorpositionfun); +GFW_EXTERN glfwSetIMECursorPositionCallback_func glfwSetIMECursorPositionCallback_impl; +#define glfwSetIMECursorPositionCallback glfwSetIMECursorPositionCallback_impl + typedef void (*glfwTerminate_func)(void); GFW_EXTERN glfwTerminate_func glfwTerminate_impl; #define glfwTerminate glfwTerminate_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index e72a87c35..784a3f347 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -507,6 +507,26 @@ has_current_selection(void) { return ans; } +void prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev); + +static bool +get_ime_cursor_position(GLFWwindow *glfw_window, GLFWIMEUpdateEvent *ev) { + if (!set_callback_window(glfw_window)) return false; + bool ans = false; + OSWindow *osw = global_state.callback_os_window; + if (osw && osw->is_focused && is_window_ready_for_callbacks()) { + Tab *tab = osw->tabs + osw->active_tab; + Window *w = tab->windows + tab->active_window; + Screen *screen = w->render_data.screen; + if (screen) { + prepare_ime_position_update_event(osw, w, screen, ev); + ans = true; + } + } + global_state.callback_os_window = NULL; + return ans; +} + static void get_window_dpi(GLFWwindow *w, double *x, double *y); @@ -832,6 +852,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { glfwSetApplicationCloseCallback(application_close_requested_callback); glfwSetCurrentSelectionCallback(get_current_selection); glfwSetHasCurrentSelectionCallback(has_current_selection); + glfwSetIMECursorPositionCallback(get_ime_cursor_position); #ifdef __APPLE__ cocoa_set_activation_policy(OPT(macos_hide_from_tasks)); glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true); diff --git a/kitty/keys.c b/kitty/keys.c index f433221c0..24f4a714d 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -102,8 +102,8 @@ update_ime_focus(OSWindow *osw, bool focused) { } 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; +prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev) { + unsigned int cell_width = osw->fonts_data->cell_width, cell_height = osw->fonts_data->cell_height; unsigned int left = w->geometry.left, top = w->geometry.top; if (screen_is_overlay_active(screen)) { left += screen->overlay_line.cursor_x * cell_width; @@ -112,8 +112,15 @@ update_ime_position(Window* w, Screen *screen) { left += screen->cursor->x * cell_width; top += screen->cursor->y * cell_height; } + ev->cursor.left = left; ev->cursor.top = top; ev->cursor.width = cell_width; ev->cursor.height = cell_height; +} + +void +update_ime_position(Window* w UNUSED, Screen *screen UNUSED) { GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION }; - ev.cursor.left = left; ev.cursor.top = top; ev.cursor.width = cell_width; ev.cursor.height = cell_height; +#ifndef __APPLE__ + prepare_ime_position_update_event(global_state.callback_os_window, w, screen, &ev); +#endif glfwUpdateIMEState(global_state.callback_os_window->handle, &ev); }