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.
This commit is contained in:
pagedown 2023-02-22 22:36:20 +08:00
parent 126aaddccb
commit 9a598237c6
No known key found for this signature in database
GPG Key ID: E921CF18AC8FF6EB
8 changed files with 66 additions and 18 deletions

View File

@ -1454,15 +1454,11 @@ is_ascii_control_char(char x) {
} }
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { 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 - (void)updateIMEStateFor:(GLFWIMEUpdateType)which
focused:(bool)focused 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) { if (which == GLFW_IME_UPDATE_FOCUS && !focused && [self hasMarkedText] && window) {
[input_context discardMarkedText]; [input_context discardMarkedText];
@ -1472,16 +1468,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
_glfw.ns.text[0] = 0; _glfw.ns.text[0] = 0;
} }
if (which != GLFW_IME_UPDATE_CURSOR_POSITION) return; 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]; if (_glfwPlatformWindowFocused(window)) [[window->ns.view inputContext] invalidateCharacterCoordinates];
} }
@ -1507,6 +1494,21 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
actualRange:(NSRangePointer)actualRange actualRange:(NSRangePointer)actualRange
{ {
(void)range; (void)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; return markedRect;
} }

2
glfw/glfw3.h vendored
View File

@ -1732,6 +1732,7 @@ typedef enum {
} GLFWClipboardType; } GLFWClipboardType;
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype); typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz); typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
typedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev);
/*! @brief Video mode type. /*! @brief Video mode type.
* *
@ -1891,6 +1892,7 @@ GLFWAPI void glfwRemoveTimer(unsigned long long);
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function); GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback); GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);
GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback); GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback);
GLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun callback);
/*! @brief Terminates the GLFW library. /*! @brief Terminates the GLFW library.
* *

7
glfw/init.c vendored
View File

@ -402,3 +402,10 @@ GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascur
_GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun); _GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun);
return 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;
}

1
glfw/internal.h vendored
View File

@ -635,6 +635,7 @@ struct _GLFWlibrary
GLFWdrawtextfun draw_text; GLFWdrawtextfun draw_text;
GLFWcurrentselectionfun get_current_selection; GLFWcurrentselectionfun get_current_selection;
GLFWhascurrentselectionfun has_current_selection; GLFWhascurrentselectionfun has_current_selection;
GLFWimecursorpositionfun get_ime_cursor_position;
} callbacks; } callbacks;

3
kitty/glfw-wrapper.c generated
View File

@ -44,6 +44,9 @@ load_glfw(const char* path) {
*(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback"); *(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback");
if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror()); 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"); *(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate");
if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror()); if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror());

5
kitty/glfw-wrapper.h generated
View File

@ -1470,6 +1470,7 @@ typedef enum {
} GLFWClipboardType; } GLFWClipboardType;
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype); typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz); typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
typedef bool (* GLFWimecursorpositionfun)(GLFWwindow *window, GLFWIMEUpdateEvent *ev);
/*! @brief Video mode type. /*! @brief Video mode type.
* *
@ -1667,6 +1668,10 @@ typedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GL
GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl; GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl;
#define glfwSetHasCurrentSelectionCallback 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); typedef void (*glfwTerminate_func)(void);
GFW_EXTERN glfwTerminate_func glfwTerminate_impl; GFW_EXTERN glfwTerminate_func glfwTerminate_impl;
#define glfwTerminate glfwTerminate_impl #define glfwTerminate glfwTerminate_impl

View File

@ -507,6 +507,26 @@ has_current_selection(void) {
return ans; 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); 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); glfwSetApplicationCloseCallback(application_close_requested_callback);
glfwSetCurrentSelectionCallback(get_current_selection); glfwSetCurrentSelectionCallback(get_current_selection);
glfwSetHasCurrentSelectionCallback(has_current_selection); glfwSetHasCurrentSelectionCallback(has_current_selection);
glfwSetIMECursorPositionCallback(get_ime_cursor_position);
#ifdef __APPLE__ #ifdef __APPLE__
cocoa_set_activation_policy(OPT(macos_hide_from_tasks)); cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true); glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);

View File

@ -102,8 +102,8 @@ update_ime_focus(OSWindow *osw, bool focused) {
} }
void void
update_ime_position(Window* w, Screen *screen) { prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev) {
unsigned int cell_width = global_state.callback_os_window->fonts_data->cell_width, cell_height = global_state.callback_os_window->fonts_data->cell_height; 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; unsigned int left = w->geometry.left, top = w->geometry.top;
if (screen_is_overlay_active(screen)) { if (screen_is_overlay_active(screen)) {
left += screen->overlay_line.cursor_x * cell_width; 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; left += screen->cursor->x * cell_width;
top += screen->cursor->y * cell_height; 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 }; 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); glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);
} }