From 456261cd7e1315e49ffc678f5bb3759db3835a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E9=9D=92=E5=B1=B1?= Date: Fri, 7 Mar 2025 15:36:01 +0800 Subject: [PATCH] Fix cannot input Chinese after restarting the input method on X11 Register a callback via `XRegisterIMInstantiateCallback()` to detect whether the input method is available again. --- platform/linuxbsd/x11/display_server_x11.cpp | 224 ++++++++++--------- platform/linuxbsd/x11/display_server_x11.h | 5 +- 2 files changed, 125 insertions(+), 104 deletions(-) diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 72f2e88ecf4..79a4e78a132 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -4143,7 +4143,6 @@ void DisplayServerX11::_xim_preedit_caret_callback(::XIM xim, ::XPointer client_ void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data, ::XPointer call_data) { - WARN_PRINT("Input method stopped"); DisplayServerX11 *ds = reinterpret_cast(client_data); ds->xim = nullptr; @@ -6047,6 +6046,73 @@ DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, W return ds; } +void DisplayServerX11::_create_xic(WindowData &wd) { + if (xim && xim_style) { + // Block events polling while changing input focus + // because it triggers some event polling internally. + MutexLock mutex_lock(events_mutex); + + // Force on-the-spot for the over-the-spot style. + if ((xim_style & XIMPreeditPosition) != 0) { + xim_style &= ~XIMPreeditPosition; + xim_style |= XIMPreeditCallbacks; + } + if ((xim_style & XIMPreeditCallbacks) != 0) { + ::XIMCallback preedit_start_callback; + preedit_start_callback.client_data = (::XPointer)(this); + preedit_start_callback.callback = (::XIMProc)(void *)(_xim_preedit_start_callback); + + ::XIMCallback preedit_done_callback; + preedit_done_callback.client_data = (::XPointer)(this); + preedit_done_callback.callback = (::XIMProc)(_xim_preedit_done_callback); + + ::XIMCallback preedit_draw_callback; + preedit_draw_callback.client_data = (::XPointer)(this); + preedit_draw_callback.callback = (::XIMProc)(_xim_preedit_draw_callback); + + ::XIMCallback preedit_caret_callback; + preedit_caret_callback.client_data = (::XPointer)(this); + preedit_caret_callback.callback = (::XIMProc)(_xim_preedit_caret_callback); + + ::XVaNestedList preedit_attributes = XVaCreateNestedList(0, + XNPreeditStartCallback, &preedit_start_callback, + XNPreeditDoneCallback, &preedit_done_callback, + XNPreeditDrawCallback, &preedit_draw_callback, + XNPreeditCaretCallback, &preedit_caret_callback, + (char *)nullptr); + + wd.xic = XCreateIC(xim, + XNInputStyle, xim_style, + XNClientWindow, wd.x11_xim_window, + XNFocusWindow, wd.x11_xim_window, + XNPreeditAttributes, preedit_attributes, + (char *)nullptr); + XFree(preedit_attributes); + } else { + wd.xic = XCreateIC(xim, + XNInputStyle, xim_style, + XNClientWindow, wd.x11_xim_window, + XNFocusWindow, wd.x11_xim_window, + (char *)nullptr); + } + + long im_event_mask = 0; + if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) { + WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value."); + XDestroyIC(wd.xic); + wd.xic = nullptr; + } + if (wd.xic) { + XUnsetICFocus(wd.xic); + } else { + WARN_PRINT("XCreateIC couldn't create wd.xic."); + } + } else { + wd.xic = nullptr; + WARN_PRINT("XCreateIC couldn't create wd.xic."); + } +} + DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, Window p_parent_window) { //Create window @@ -6252,70 +6318,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1); } - if (xim && xim_style) { - // Block events polling while changing input focus - // because it triggers some event polling internally. - MutexLock mutex_lock(events_mutex); - - // Force on-the-spot for the over-the-spot style. - if ((xim_style & XIMPreeditPosition) != 0) { - xim_style &= ~XIMPreeditPosition; - xim_style |= XIMPreeditCallbacks; - } - if ((xim_style & XIMPreeditCallbacks) != 0) { - ::XIMCallback preedit_start_callback; - preedit_start_callback.client_data = (::XPointer)(this); - preedit_start_callback.callback = (::XIMProc)(void *)(_xim_preedit_start_callback); - - ::XIMCallback preedit_done_callback; - preedit_done_callback.client_data = (::XPointer)(this); - preedit_done_callback.callback = (::XIMProc)(_xim_preedit_done_callback); - - ::XIMCallback preedit_draw_callback; - preedit_draw_callback.client_data = (::XPointer)(this); - preedit_draw_callback.callback = (::XIMProc)(_xim_preedit_draw_callback); - - ::XIMCallback preedit_caret_callback; - preedit_caret_callback.client_data = (::XPointer)(this); - preedit_caret_callback.callback = (::XIMProc)(_xim_preedit_caret_callback); - - ::XVaNestedList preedit_attributes = XVaCreateNestedList(0, - XNPreeditStartCallback, &preedit_start_callback, - XNPreeditDoneCallback, &preedit_done_callback, - XNPreeditDrawCallback, &preedit_draw_callback, - XNPreeditCaretCallback, &preedit_caret_callback, - (char *)nullptr); - - wd.xic = XCreateIC(xim, - XNInputStyle, xim_style, - XNClientWindow, wd.x11_xim_window, - XNFocusWindow, wd.x11_xim_window, - XNPreeditAttributes, preedit_attributes, - (char *)nullptr); - XFree(preedit_attributes); - } else { - wd.xic = XCreateIC(xim, - XNInputStyle, xim_style, - XNClientWindow, wd.x11_xim_window, - XNFocusWindow, wd.x11_xim_window, - (char *)nullptr); - } - - if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) { - WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value"); - XDestroyIC(wd.xic); - wd.xic = nullptr; - } - if (wd.xic) { - XUnsetICFocus(wd.xic); - } else { - WARN_PRINT("XCreateIC couldn't create wd.xic"); - } - } else { - wd.xic = nullptr; - WARN_PRINT("XCreateIC couldn't create wd.xic"); - } - + _create_xic(wd); _update_context(wd); if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) { @@ -6471,6 +6474,54 @@ static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMSt return p_style_a; } +void DisplayServerX11::_xim_instantiate_callback(::Display *display, ::XPointer client_data, + ::XPointer call_data) { + DisplayServerX11 *ds = reinterpret_cast(client_data); + + ds->xim = XOpenIM(display, nullptr, nullptr, nullptr); + + if (ds->xim == nullptr) { + WARN_PRINT("XOpenIM failed."); + ds->xim_style = 0L; + } else { + ::XIMCallback im_destroy_callback; + im_destroy_callback.client_data = client_data; + im_destroy_callback.callback = (::XIMProc)(_xim_destroy_callback); + if (XSetIMValues(ds->xim, XNDestroyCallback, &im_destroy_callback, + nullptr) != nullptr) { + WARN_PRINT("Error setting XIM destroy callback."); + } + + ::XIMStyles *xim_styles = nullptr; + ds->xim_style = 0L; + char *imvalret = XGetIMValues(ds->xim, XNQueryInputStyle, &xim_styles, nullptr); + if (imvalret != nullptr || xim_styles == nullptr) { + fprintf(stderr, "Input method doesn't support any styles\n"); + } + + if (xim_styles) { + ds->xim_style = 0L; + for (int i = 0; i < xim_styles->count_styles; i++) { + const ::XIMStyle &style = xim_styles->supported_styles[i]; + + if (!_is_xim_style_supported(style)) { + continue; + } + + ds->xim_style = _get_best_xim_style(ds->xim_style, style); + } + + XFree(xim_styles); + } + XFree(imvalret); + } + + // The input method has been (re)started. + for (KeyValue &E : ds->windows) { + ds->_create_xic(E.value); + } +} + DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) { KeyMappingX11::initialize(); @@ -6663,10 +6714,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode if (modifiers == nullptr) { if (OS::get_singleton()->is_stdout_verbose()) { - WARN_PRINT("IME is disabled"); + WARN_PRINT("IME is disabled."); } XSetLocaleModifiers("@im=none"); - WARN_PRINT("Error setting locale modifiers"); + WARN_PRINT("Error setting locale modifiers."); } const char *err; @@ -6711,42 +6762,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode return; } - xim = XOpenIM(x11_display, nullptr, nullptr, nullptr); - - if (xim == nullptr) { - WARN_PRINT("XOpenIM failed"); - xim_style = 0L; - } else { - ::XIMCallback im_destroy_callback; - im_destroy_callback.client_data = (::XPointer)(this); - im_destroy_callback.callback = (::XIMProc)(_xim_destroy_callback); - if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback, - nullptr) != nullptr) { - WARN_PRINT("Error setting XIM destroy callback"); - } - - ::XIMStyles *xim_styles = nullptr; - xim_style = 0L; - char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, nullptr); - if (imvalret != nullptr || xim_styles == nullptr) { - fprintf(stderr, "Input method doesn't support any styles\n"); - } - - if (xim_styles) { - xim_style = 0L; - for (int i = 0; i < xim_styles->count_styles; i++) { - const ::XIMStyle &style = xim_styles->supported_styles[i]; - - if (!_is_xim_style_supported(style)) { - continue; - } - - xim_style = _get_best_xim_style(xim_style, style); - } - - XFree(xim_styles); - } - XFree(imvalret); + if (!XRegisterIMInstantiateCallback(x11_display, nullptr, nullptr, nullptr, (::XIDProc)(_xim_instantiate_callback), (::XPointer)this)) { + WARN_PRINT("Error registering XIM instantiate callback."); + _xim_instantiate_callback(x11_display, (::XPointer)this, nullptr); } /* Atom internment */ diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 80db727b978..9a39a6d9486 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -236,6 +236,7 @@ class DisplayServerX11 : public DisplayServer { WindowID last_focused_window = INVALID_WINDOW_ID; WindowID window_id_counter = MAIN_WINDOW_ID; + void _create_xic(WindowData &wd); WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, Window p_parent_window); String internal_clipboard; @@ -246,7 +247,7 @@ class DisplayServerX11 : public DisplayServer { int xmblen = 0; unsigned long last_timestamp = 0; ::Time last_keyrelease_time = 0; - ::XIM xim; + ::XIM xim = nullptr; ::XIMStyle xim_style; static int _xim_preedit_start_callback(::XIM xim, ::XPointer client_data, @@ -259,6 +260,8 @@ class DisplayServerX11 : public DisplayServer { ::XIMPreeditCaretCallbackStruct *call_data); static void _xim_destroy_callback(::XIM im, ::XPointer client_data, ::XPointer call_data); + static void _xim_instantiate_callback(::Display *display, ::XPointer client_data, + ::XPointer call_data); Point2i last_mouse_pos; bool last_mouse_pos_valid = false;