From a8a65b6fca6b9d12defcc0aa334071c30f2650a2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 9 Jan 2025 16:37:11 -0800 Subject: [PATCH] Use XIM for IME input on X11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested with fcitx5 and ibus on Xorg and Xwayland * Used US English with dead keys and verified that ` followed by a results in à * Used Hangul to enter Korean and got text in the expected order * Used the mozc IM to enter Japanese and was able to generate candidates and so forth Fixes https://github.com/libsdl-org/SDL/issues/3907 Fixes https://github.com/libsdl-org/SDL/issues/6164 Fixes https://github.com/libsdl-org/SDL/issues/11894 --- src/video/x11/SDL_x11events.c | 92 ++++++++------------------------- src/video/x11/SDL_x11keyboard.c | 33 +----------- 2 files changed, 22 insertions(+), 103 deletions(-) diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 3641cf9c35..95f2dc2197 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -438,9 +438,6 @@ static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data) if (data->ic) { X11_XSetICFocus(data->ic); } -#endif -#ifdef SDL_USE_IME - SDL_IME_SetFocus(true); #endif if (data->flashing_window) { X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL); @@ -464,9 +461,6 @@ static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data) X11_XUnsetICFocus(data->ic); } #endif -#ifdef SDL_USE_IME - SDL_IME_SetFocus(false); -#endif } static void X11_DispatchMapNotify(SDL_WindowData *data) @@ -893,13 +887,15 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ char text[64]; Status status = 0; bool handled_by_ime = false; + bool pressed = (xevent->type == KeyPress); + SDL_Scancode scancode = videodata->key_layout[keycode]; Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time); #ifdef DEBUG_XEVENTS SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)\n", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode); #endif #ifdef DEBUG_SCANCODES - if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) { + if (scancode == SDL_SCANCODE_UNKNOWN && keycode) { int min_keycode, max_keycode; X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13); @@ -913,61 +909,34 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ X11_UpdateSystemKeyModifiers(videodata); if (SDL_TextInputActive(windowdata->window)) { -#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX) - /* Save the original keycode for dead keys, which are filtered out by - the XFilterEvent() call below. - */ - int orig_event_type = xevent->type; - KeyCode orig_keycode = xevent->xkey.keycode; -#endif - // filter events catches XIM events and sends them to the correct handler if (X11_XFilterEvent(xevent, None)) { #ifdef DEBUG_XEVENTS SDL_Log("Filtered event type = %d display = %p window = 0x%lx\n", xevent->type, xevent->xany.display, xevent->xany.window); #endif - // Make sure dead key press/release events are sent - /* But only if we're using one of the DBus IMEs, otherwise - some XIM IMEs will generate duplicate events */ -#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX) - SDL_Scancode scancode = videodata->key_layout[orig_keycode]; - videodata->filter_code = orig_keycode; - videodata->filter_time = xevent->xkey.time; - if (orig_event_type == KeyPress) { - X11_HandleModifierKeys(videodata, scancode, true, true); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, orig_keycode, scancode, true); - } else { - X11_HandleModifierKeys(videodata, scancode, false, true); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, orig_keycode, scancode, false); - } -#endif - return; + handled_by_ime = true; } + if (!handled_by_ime) { #ifdef X_HAVE_UTF8_STRING - if (windowdata->ic && xevent->type == KeyPress) { - text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1, - &keysym, &status); - } else { - text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); - } + if (windowdata->ic && xevent->type == KeyPress) { + text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1, + &keysym, &status); + } else { + text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); + } #else - text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); -#endif - -#ifdef SDL_USE_IME - handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode, (xevent->type == KeyPress)); + text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); #endif + } } if (!handled_by_ime) { - if (xevent->type == KeyPress) { - // Don't send the key if it looks like a duplicate of a filtered key sent by an IME - if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) { - X11_HandleModifierKeys(videodata, videodata->key_layout[keycode], true, true); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, videodata->key_layout[keycode], true); - } + if (pressed) { + X11_HandleModifierKeys(videodata, scancode, true, true); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true); + if (*text) { text[text_length] = '\0'; SDL_SendKeyboardText(text); @@ -977,12 +946,13 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ // We're about to get a repeated key down, ignore the key up return; } - X11_HandleModifierKeys(videodata, videodata->key_layout[keycode], false, true); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, videodata->key_layout[keycode], false); + + X11_HandleModifierKeys(videodata, scancode, false, true); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false); } } - if (xevent->type == KeyPress) { + if (pressed) { X11_UpdateUserTime(windowdata, xevent->xkey.time); } } @@ -1462,12 +1432,6 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y); SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y); -#ifdef SDL_USE_IME - if (SDL_TextInputActive(data->window)) { - // Update IME candidate list position - SDL_IME_UpdateTextInputArea(NULL); - } -#endif for (w = data->window->first_child; w; w = w->next_sibling) { // Don't update hidden child popup windows, their relative position doesn't change if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) { @@ -2109,13 +2073,6 @@ int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) X11_DispatchEvent(_this, &xevent); -#ifdef SDL_USE_IME - SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); - if (keyboard_focus && SDL_TextInputActive(keyboard_focus)) { - SDL_IME_PumpEvents(); - } -#endif - #ifdef SDL_USE_LIBDBUS SDL_DBus_PumpEvents(); #endif @@ -2170,13 +2127,6 @@ void X11_PumpEvents(SDL_VideoDevice *_this) X11_DispatchEvent(_this, &xevent); } -#ifdef SDL_USE_IME - SDL_Window *keyboard_focus = SDL_GetKeyboardFocus(); - if (keyboard_focus && SDL_TextInputActive(keyboard_focus)) { - SDL_IME_PumpEvents(); - } -#endif - #ifdef SDL_USE_LIBDBUS SDL_DBus_PumpEvents(); #endif diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index d1d60f30c5..2d25ec2de5 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -169,9 +169,6 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) Compose keys will work correctly. */ char *prev_locale = setlocale(LC_ALL, NULL); char *prev_xmods = X11_XSetLocaleModifiers(NULL); - const char *new_xmods = ""; - const char *env_xmods = SDL_getenv("XMODIFIERS"); - bool has_dbus_ime_support = false; if (prev_locale) { prev_locale = SDL_strdup(prev_locale); @@ -181,22 +178,8 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) prev_xmods = SDL_strdup(prev_xmods); } - /* IBus resends some key events that were filtered by XFilterEvents - when it is used via XIM which causes issues. Prevent this by forcing - @im=none if XMODIFIERS contains @im=ibus. IBus can still be used via - the DBus implementation, which also has support for pre-editing. */ - if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) { - has_dbus_ime_support = true; - } - if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) { - has_dbus_ime_support = true; - } - if (has_dbus_ime_support || !xkb_repeat) { - new_xmods = "@im=none"; - } - (void)setlocale(LC_ALL, ""); - X11_XSetLocaleModifiers(new_xmods); + X11_XSetLocaleModifiers(""); data->im = X11_XOpenIM(data->display, NULL, NULL, NULL); @@ -318,10 +301,6 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); -#ifdef SDL_USE_IME - SDL_IME_Init(); -#endif - X11_ReconcileKeyboardState(_this); return true; @@ -470,10 +449,6 @@ void X11_QuitKeyboard(SDL_VideoDevice *_this) data->xkb.desc_ptr = NULL; } #endif - -#ifdef SDL_USE_IME - SDL_IME_Quit(); -#endif } static void X11_ResetXIM(SDL_VideoDevice *_this, SDL_Window *window) @@ -501,17 +476,11 @@ bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) { X11_ResetXIM(_this, window); -#ifdef SDL_USE_IME - SDL_IME_Reset(); -#endif return true; } bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window) { -#ifdef SDL_USE_IME - SDL_IME_UpdateTextInputArea(window); -#endif return true; }