From 5fe984978cea857ea57c8f4942c4a7496f72e07d Mon Sep 17 00:00:00 2001 From: Alex Baines Date: Fri, 28 Oct 2016 01:28:58 +0100 Subject: [PATCH] Fix double events / no repeat flag on key events when built withoutibus/fcitx Uses XkbSetDetectableKeyRepeat, and falls back to forcing @im=none if it's not supported. --- src/video/x11/SDL_x11events.c | 6 ++- src/video/x11/SDL_x11keyboard.c | 83 +++++++++++++++++++++++++++++---- src/video/x11/SDL_x11sym.h | 1 + src/video/x11/SDL_x11video.c | 63 ------------------------- 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 56d2368a0..74e54d9d9 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -568,14 +568,18 @@ X11_DispatchEvent(_THIS) printf("Filtered event type = %d display = %d window = %d\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 (orig_keycode) { - /* Make sure dead key press/release events are sent */ +#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H) SDL_Scancode scancode = videodata->key_layout[orig_keycode]; if (orig_event_type == KeyPress) { SDL_SendKeyboardKey(SDL_PRESSED, scancode); } else { SDL_SendKeyboardKey(SDL_RELEASED, scancode); } +#endif } return; } diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index b888bff35..acc15d7c9 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -33,6 +33,10 @@ #include "imKStoUCS.h" +#ifdef X_HAVE_UTF8_STRING +#include +#endif + /* *INDENT-OFF* */ static const struct { KeySym keysym; @@ -262,19 +266,82 @@ X11_InitKeyboard(_THIS) int best_distance; int best_index; int distance; - + BOOL xkb_repeat = 0; + X11_XAutoRepeatOn(data->display); #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM { - int xkb_major = XkbMajorVersion; - int xkb_minor = XkbMinorVersion; - if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) { - data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd); - } - } -#endif + int xkb_major = XkbMajorVersion; + int xkb_minor = XkbMinorVersion; + if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) { + data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd); + } + + /* This will remove KeyRelease events for held keys */ + X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat); + } +#endif + + /* Open a connection to the X input manager */ +#ifdef X_HAVE_UTF8_STRING + if (SDL_X11_HAVE_UTF8) { + /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that + Compose keys will work correctly. */ + char *prev_locale = setlocale(LC_ALL, NULL); + char *prev_xmods = X11_XSetLocaleModifiers(NULL); + const char *new_xmods = ""; +#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H) + const char *env_xmods = SDL_getenv("XMODIFIERS"); +#endif + SDL_bool has_dbus_ime_support = SDL_FALSE; + + if (prev_locale) { + prev_locale = SDL_strdup(prev_locale); + } + + if (prev_xmods) { + 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. */ +#ifdef HAVE_IBUS_IBUS_H + if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) { + has_dbus_ime_support = SDL_TRUE; + } +#endif +#ifdef HAVE_FCITX_FRONTEND_H + if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) { + has_dbus_ime_support = SDL_TRUE; + } +#endif + if (has_dbus_ime_support || !xkb_repeat) { + new_xmods = "@im=none"; + } + + setlocale(LC_ALL, ""); + X11_XSetLocaleModifiers(new_xmods); + + data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname); + + /* Reset the locale + X locale modifiers back to how they were, + locale first because the X locale modifiers depend on it. */ + setlocale(LC_ALL, prev_locale); + X11_XSetLocaleModifiers(prev_xmods); + + if (prev_locale) { + SDL_free(prev_locale); + } + + if (prev_xmods) { + SDL_free(prev_xmods); + } + } +#endif /* Try to determine which scancodes are being used based on fingerprint */ best_distance = SDL_arraysize(fingerprint) + 1; best_index = -1; diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 7290412b7..371f6a4ff 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -179,6 +179,7 @@ SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c),(a,b,c) SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c),(a,b,c),return) SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c),(a,b,c),return) SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),) +SDL_X11_SYM(BOOL,XkbSetDetectableAutoRepeat,(Display* a, BOOL b, BOOL* c),(a,b,c),return) #endif #if NeedWidePrototypes diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index 38b34de34..24043ff4e 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -39,10 +39,6 @@ #include "SDL_x11opengles.h" #endif -#ifdef X_HAVE_UTF8_STRING -#include -#endif - /* Initialization/Query functions */ static int X11_VideoInit(_THIS); static void X11_VideoQuit(_THIS); @@ -388,65 +384,6 @@ X11_VideoInit(_THIS) /* I have no idea how random this actually is, or has to be. */ data->window_group = (XID) (((size_t) data->pid) ^ ((size_t) _this)); - /* Open a connection to the X input manager */ -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that - Compose keys will work correctly. */ - char *prev_locale = setlocale(LC_ALL, NULL); - char *prev_xmods = X11_XSetLocaleModifiers(NULL); - const char *new_xmods = ""; -#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H) - const char *env_xmods = SDL_getenv("XMODIFIERS"); -#endif - SDL_bool has_dbus_ime_support = SDL_FALSE; - - if (prev_locale) { - prev_locale = SDL_strdup(prev_locale); - } - - if (prev_xmods) { - 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. */ -#ifdef HAVE_IBUS_IBUS_H - if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) { - has_dbus_ime_support = SDL_TRUE; - } -#endif -#ifdef HAVE_FCITX_FRONTEND_H - if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) { - has_dbus_ime_support = SDL_TRUE; - } -#endif - if (has_dbus_ime_support) { - new_xmods = "@im=none"; - } - - setlocale(LC_ALL, ""); - X11_XSetLocaleModifiers(new_xmods); - - data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname); - - /* Reset the locale + X locale modifiers back to how they were, - locale first because the X locale modifiers depend on it. */ - setlocale(LC_ALL, prev_locale); - X11_XSetLocaleModifiers(prev_xmods); - - if (prev_locale) { - SDL_free(prev_locale); - } - - if (prev_xmods) { - SDL_free(prev_xmods); - } - } -#endif - /* Look up some useful Atoms */ #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False) GET_ATOM(WM_PROTOCOLS);