src/video/x11/SDL_x11keyboard.c
author Alex Baines <alex@abaines.me.uk>
Fri, 28 Oct 2016 01:28:58 +0100
changeset 10562 b48d8a98e261
parent 10548 aea47b61c640
child 10569 9429d331668e
permissions -rw-r--r--
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.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include "SDL_x11video.h"
    26 
    27 #include "../../events/SDL_keyboard_c.h"
    28 #include "../../events/scancodes_darwin.h"
    29 #include "../../events/scancodes_xfree86.h"
    30 
    31 #include <X11/keysym.h>
    32 #include <X11/XKBlib.h>
    33 
    34 #include "imKStoUCS.h"
    35 
    36 #ifdef X_HAVE_UTF8_STRING
    37 #include <locale.h>
    38 #endif
    39 
    40 /* *INDENT-OFF* */
    41 static const struct {
    42     KeySym keysym;
    43     SDL_Scancode scancode;
    44 } KeySymToSDLScancode[] = {
    45     { XK_Return, SDL_SCANCODE_RETURN },
    46     { XK_Escape, SDL_SCANCODE_ESCAPE },
    47     { XK_BackSpace, SDL_SCANCODE_BACKSPACE },
    48     { XK_Tab, SDL_SCANCODE_TAB },
    49     { XK_Caps_Lock, SDL_SCANCODE_CAPSLOCK },
    50     { XK_F1, SDL_SCANCODE_F1 },
    51     { XK_F2, SDL_SCANCODE_F2 },
    52     { XK_F3, SDL_SCANCODE_F3 },
    53     { XK_F4, SDL_SCANCODE_F4 },
    54     { XK_F5, SDL_SCANCODE_F5 },
    55     { XK_F6, SDL_SCANCODE_F6 },
    56     { XK_F7, SDL_SCANCODE_F7 },
    57     { XK_F8, SDL_SCANCODE_F8 },
    58     { XK_F9, SDL_SCANCODE_F9 },
    59     { XK_F10, SDL_SCANCODE_F10 },
    60     { XK_F11, SDL_SCANCODE_F11 },
    61     { XK_F12, SDL_SCANCODE_F12 },
    62     { XK_Print, SDL_SCANCODE_PRINTSCREEN },
    63     { XK_Scroll_Lock, SDL_SCANCODE_SCROLLLOCK },
    64     { XK_Pause, SDL_SCANCODE_PAUSE },
    65     { XK_Insert, SDL_SCANCODE_INSERT },
    66     { XK_Home, SDL_SCANCODE_HOME },
    67     { XK_Prior, SDL_SCANCODE_PAGEUP },
    68     { XK_Delete, SDL_SCANCODE_DELETE },
    69     { XK_End, SDL_SCANCODE_END },
    70     { XK_Next, SDL_SCANCODE_PAGEDOWN },
    71     { XK_Right, SDL_SCANCODE_RIGHT },
    72     { XK_Left, SDL_SCANCODE_LEFT },
    73     { XK_Down, SDL_SCANCODE_DOWN },
    74     { XK_Up, SDL_SCANCODE_UP },
    75     { XK_Num_Lock, SDL_SCANCODE_NUMLOCKCLEAR },
    76     { XK_KP_Divide, SDL_SCANCODE_KP_DIVIDE },
    77     { XK_KP_Multiply, SDL_SCANCODE_KP_MULTIPLY },
    78     { XK_KP_Subtract, SDL_SCANCODE_KP_MINUS },
    79     { XK_KP_Add, SDL_SCANCODE_KP_PLUS },
    80     { XK_KP_Enter, SDL_SCANCODE_KP_ENTER },
    81     { XK_KP_Delete, SDL_SCANCODE_KP_PERIOD },
    82     { XK_KP_End, SDL_SCANCODE_KP_1 },
    83     { XK_KP_Down, SDL_SCANCODE_KP_2 },
    84     { XK_KP_Next, SDL_SCANCODE_KP_3 },
    85     { XK_KP_Left, SDL_SCANCODE_KP_4 },
    86     { XK_KP_Begin, SDL_SCANCODE_KP_5 },
    87     { XK_KP_Right, SDL_SCANCODE_KP_6 },
    88     { XK_KP_Home, SDL_SCANCODE_KP_7 },
    89     { XK_KP_Up, SDL_SCANCODE_KP_8 },
    90     { XK_KP_Prior, SDL_SCANCODE_KP_9 },
    91     { XK_KP_Insert, SDL_SCANCODE_KP_0 },
    92     { XK_KP_Decimal, SDL_SCANCODE_KP_PERIOD },
    93     { XK_KP_1, SDL_SCANCODE_KP_1 },
    94     { XK_KP_2, SDL_SCANCODE_KP_2 },
    95     { XK_KP_3, SDL_SCANCODE_KP_3 },
    96     { XK_KP_4, SDL_SCANCODE_KP_4 },
    97     { XK_KP_5, SDL_SCANCODE_KP_5 },
    98     { XK_KP_6, SDL_SCANCODE_KP_6 },
    99     { XK_KP_7, SDL_SCANCODE_KP_7 },
   100     { XK_KP_8, SDL_SCANCODE_KP_8 },
   101     { XK_KP_9, SDL_SCANCODE_KP_9 },
   102     { XK_KP_0, SDL_SCANCODE_KP_0 },
   103     { XK_KP_Decimal, SDL_SCANCODE_KP_PERIOD },
   104     { XK_Hyper_R, SDL_SCANCODE_APPLICATION },
   105     { XK_KP_Equal, SDL_SCANCODE_KP_EQUALS },
   106     { XK_F13, SDL_SCANCODE_F13 },
   107     { XK_F14, SDL_SCANCODE_F14 },
   108     { XK_F15, SDL_SCANCODE_F15 },
   109     { XK_F16, SDL_SCANCODE_F16 },
   110     { XK_F17, SDL_SCANCODE_F17 },
   111     { XK_F18, SDL_SCANCODE_F18 },
   112     { XK_F19, SDL_SCANCODE_F19 },
   113     { XK_F20, SDL_SCANCODE_F20 },
   114     { XK_F21, SDL_SCANCODE_F21 },
   115     { XK_F22, SDL_SCANCODE_F22 },
   116     { XK_F23, SDL_SCANCODE_F23 },
   117     { XK_F24, SDL_SCANCODE_F24 },
   118     { XK_Execute, SDL_SCANCODE_EXECUTE },
   119     { XK_Help, SDL_SCANCODE_HELP },
   120     { XK_Menu, SDL_SCANCODE_MENU },
   121     { XK_Select, SDL_SCANCODE_SELECT },
   122     { XK_Cancel, SDL_SCANCODE_STOP },
   123     { XK_Redo, SDL_SCANCODE_AGAIN },
   124     { XK_Undo, SDL_SCANCODE_UNDO },
   125     { XK_Find, SDL_SCANCODE_FIND },
   126     { XK_KP_Separator, SDL_SCANCODE_KP_COMMA },
   127     { XK_Sys_Req, SDL_SCANCODE_SYSREQ },
   128     { XK_Control_L, SDL_SCANCODE_LCTRL },
   129     { XK_Shift_L, SDL_SCANCODE_LSHIFT },
   130     { XK_Alt_L, SDL_SCANCODE_LALT },
   131     { XK_Meta_L, SDL_SCANCODE_LGUI },
   132     { XK_Super_L, SDL_SCANCODE_LGUI },
   133     { XK_Control_R, SDL_SCANCODE_RCTRL },
   134     { XK_Shift_R, SDL_SCANCODE_RSHIFT },
   135     { XK_Alt_R, SDL_SCANCODE_RALT },
   136     { XK_ISO_Level3_Shift, SDL_SCANCODE_RALT },
   137     { XK_Meta_R, SDL_SCANCODE_RGUI },
   138     { XK_Super_R, SDL_SCANCODE_RGUI },
   139     { XK_Mode_switch, SDL_SCANCODE_MODE },
   140     { XK_period, SDL_SCANCODE_PERIOD },
   141     { XK_comma, SDL_SCANCODE_COMMA },
   142     { XK_slash, SDL_SCANCODE_SLASH },
   143     { XK_backslash, SDL_SCANCODE_BACKSLASH },
   144     { XK_minus, SDL_SCANCODE_MINUS },
   145     { XK_equal, SDL_SCANCODE_EQUALS },
   146     { XK_space, SDL_SCANCODE_SPACE },
   147     { XK_grave, SDL_SCANCODE_GRAVE },
   148     { XK_apostrophe, SDL_SCANCODE_APOSTROPHE },
   149     { XK_bracketleft, SDL_SCANCODE_LEFTBRACKET },
   150     { XK_bracketright, SDL_SCANCODE_RIGHTBRACKET },
   151 };
   152 
   153 static const struct
   154 {
   155     SDL_Scancode const *table;
   156     int table_size;
   157 } scancode_set[] = {
   158     { darwin_scancode_table, SDL_arraysize(darwin_scancode_table) },
   159     { xfree86_scancode_table, SDL_arraysize(xfree86_scancode_table) },
   160     { xfree86_scancode_table2, SDL_arraysize(xfree86_scancode_table2) },
   161     { xvnc_scancode_table, SDL_arraysize(xvnc_scancode_table) },
   162 };
   163 /* *INDENT-OFF* */
   164 
   165 /* This function only works for keyboards in US QWERTY layout */
   166 static SDL_Scancode
   167 X11_KeyCodeToSDLScancode(_THIS, KeyCode keycode)
   168 {
   169     KeySym keysym;
   170     int i;
   171 
   172     keysym = X11_KeyCodeToSym(_this, keycode, 0);
   173     if (keysym == NoSymbol) {
   174         return SDL_SCANCODE_UNKNOWN;
   175     }
   176 
   177     if (keysym >= XK_a && keysym <= XK_z) {
   178         return SDL_SCANCODE_A + (keysym - XK_a);
   179     }
   180     if (keysym >= XK_A && keysym <= XK_Z) {
   181         return SDL_SCANCODE_A + (keysym - XK_A);
   182     }
   183 
   184     if (keysym == XK_0) {
   185         return SDL_SCANCODE_0;
   186     }
   187     if (keysym >= XK_1 && keysym <= XK_9) {
   188         return SDL_SCANCODE_1 + (keysym - XK_1);
   189     }
   190 
   191     for (i = 0; i < SDL_arraysize(KeySymToSDLScancode); ++i) {
   192         if (keysym == KeySymToSDLScancode[i].keysym) {
   193             return KeySymToSDLScancode[i].scancode;
   194         }
   195     }
   196     return SDL_SCANCODE_UNKNOWN;
   197 }
   198 
   199 static Uint32
   200 X11_KeyCodeToUcs4(_THIS, KeyCode keycode, unsigned char group)
   201 {
   202     KeySym keysym = X11_KeyCodeToSym(_this, keycode, group);
   203 
   204     if (keysym == NoSymbol) {
   205         return 0;
   206     }
   207 
   208     return X11_KeySymToUcs4(keysym);
   209 }
   210 
   211 KeySym
   212 X11_KeyCodeToSym(_THIS, KeyCode keycode, unsigned char group)
   213 {
   214     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   215     KeySym keysym;
   216 
   217 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
   218     if (data->xkb) {
   219         int num_groups     = XkbKeyNumGroups(data->xkb, keycode);
   220         unsigned char info = XkbKeyGroupInfo(data->xkb, keycode);
   221         
   222         if (num_groups && group >= num_groups) {
   223         
   224             int action = XkbOutOfRangeGroupAction(info);
   225             
   226             if (action == XkbRedirectIntoRange) {
   227                 if ((group = XkbOutOfRangeGroupNumber(info)) >= num_groups) {
   228                     group = 0;
   229                 }
   230             } else if (action == XkbClampIntoRange) {
   231                 group = num_groups - 1;
   232             } else {
   233                 group %= num_groups;
   234             }
   235         }
   236         keysym = X11_XkbKeycodeToKeysym(data->display, keycode, group, 0);
   237     } else {
   238         keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
   239     }
   240 #else
   241     keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
   242 #endif
   243 
   244     return keysym;
   245 }
   246 
   247 int
   248 X11_InitKeyboard(_THIS)
   249 {
   250     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   251     int i = 0;
   252     int j = 0;
   253     int min_keycode, max_keycode;
   254     struct {
   255         SDL_Scancode scancode;
   256         KeySym keysym;
   257         int value;
   258     } fingerprint[] = {
   259         { SDL_SCANCODE_HOME, XK_Home, 0 },
   260         { SDL_SCANCODE_PAGEUP, XK_Prior, 0 },
   261         { SDL_SCANCODE_UP, XK_Up, 0 },
   262         { SDL_SCANCODE_LEFT, XK_Left, 0 },
   263         { SDL_SCANCODE_DELETE, XK_Delete, 0 },
   264         { SDL_SCANCODE_KP_ENTER, XK_KP_Enter, 0 },
   265     };
   266     int best_distance;
   267     int best_index;
   268     int distance;
   269     BOOL xkb_repeat = 0;
   270     
   271     X11_XAutoRepeatOn(data->display);
   272 
   273 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
   274     {
   275         int xkb_major = XkbMajorVersion;
   276         int xkb_minor = XkbMinorVersion;
   277 
   278         if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) {
   279             data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
   280         }
   281 
   282         /* This will remove KeyRelease events for held keys */
   283         X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
   284     }
   285 #endif
   286     
   287     /* Open a connection to the X input manager */
   288 #ifdef X_HAVE_UTF8_STRING
   289     if (SDL_X11_HAVE_UTF8) {
   290         /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that 
   291            Compose keys will work correctly. */
   292         char *prev_locale = setlocale(LC_ALL, NULL);
   293         char *prev_xmods  = X11_XSetLocaleModifiers(NULL);
   294         const char *new_xmods = "";
   295 #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H)
   296         const char *env_xmods = SDL_getenv("XMODIFIERS");
   297 #endif
   298         SDL_bool has_dbus_ime_support = SDL_FALSE;
   299 
   300         if (prev_locale) {
   301             prev_locale = SDL_strdup(prev_locale);
   302         }
   303 
   304         if (prev_xmods) {
   305             prev_xmods = SDL_strdup(prev_xmods);
   306         }
   307 
   308         /* IBus resends some key events that were filtered by XFilterEvents
   309            when it is used via XIM which causes issues. Prevent this by forcing
   310            @im=none if XMODIFIERS contains @im=ibus. IBus can still be used via 
   311            the DBus implementation, which also has support for pre-editing. */
   312 #ifdef HAVE_IBUS_IBUS_H
   313         if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) {
   314             has_dbus_ime_support = SDL_TRUE;
   315         }
   316 #endif
   317 #ifdef HAVE_FCITX_FRONTEND_H
   318         if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) {
   319             has_dbus_ime_support = SDL_TRUE;
   320         }
   321 #endif
   322         if (has_dbus_ime_support || !xkb_repeat) {
   323             new_xmods = "@im=none";
   324         }
   325 
   326         setlocale(LC_ALL, "");
   327         X11_XSetLocaleModifiers(new_xmods);
   328 
   329         data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname);
   330 
   331         /* Reset the locale + X locale modifiers back to how they were,
   332            locale first because the X locale modifiers depend on it. */
   333         setlocale(LC_ALL, prev_locale);
   334         X11_XSetLocaleModifiers(prev_xmods);
   335 
   336         if (prev_locale) {
   337             SDL_free(prev_locale);
   338         }
   339 
   340         if (prev_xmods) {
   341             SDL_free(prev_xmods);
   342         }
   343     }
   344 #endif
   345     /* Try to determine which scancodes are being used based on fingerprint */
   346     best_distance = SDL_arraysize(fingerprint) + 1;
   347     best_index = -1;
   348     X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode);
   349     for (i = 0; i < SDL_arraysize(fingerprint); ++i) {
   350         fingerprint[i].value =
   351             X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) -
   352             min_keycode;
   353     }
   354     for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
   355         /* Make sure the scancode set isn't too big */
   356         if ((max_keycode - min_keycode + 1) <= scancode_set[i].table_size) {
   357             continue;
   358         }
   359         distance = 0;
   360         for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
   361             if (fingerprint[j].value < 0
   362                 || fingerprint[j].value >= scancode_set[i].table_size) {
   363                 distance += 1;
   364             } else if (scancode_set[i].table[fingerprint[j].value] != fingerprint[j].scancode) {
   365                 distance += 1;
   366             }
   367         }
   368         if (distance < best_distance) {
   369             best_distance = distance;
   370             best_index = i;
   371         }
   372     }
   373     if (best_index >= 0 && best_distance <= 2) {
   374 #ifdef DEBUG_KEYBOARD
   375         printf("Using scancode set %d, min_keycode = %d, max_keycode = %d, table_size = %d\n", best_index, min_keycode, max_keycode, scancode_set[best_index].table_size);
   376 #endif
   377         SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table,
   378                    sizeof(SDL_Scancode) * scancode_set[best_index].table_size);
   379     } else {
   380         SDL_Keycode keymap[SDL_NUM_SCANCODES];
   381 
   382         printf
   383             ("Keyboard layout unknown, please send the following to the SDL mailing list (sdl@libsdl.org):\n");
   384 
   385         /* Determine key_layout - only works on US QWERTY layout */
   386         SDL_GetDefaultKeymap(keymap);
   387         for (i = min_keycode; i <= max_keycode; ++i) {
   388             KeySym sym;
   389             sym = X11_KeyCodeToSym(_this, (KeyCode) i, 0);
   390             if (sym != NoSymbol) {
   391                 SDL_Scancode scancode;
   392                 printf("code = %d, sym = 0x%X (%s) ", i - min_keycode,
   393                        (unsigned int) sym, X11_XKeysymToString(sym));
   394                 scancode = X11_KeyCodeToSDLScancode(_this, i);
   395                 data->key_layout[i] = scancode;
   396                 if (scancode == SDL_SCANCODE_UNKNOWN) {
   397                     printf("scancode not found\n");
   398                 } else {
   399                     printf("scancode = %d (%s)\n", scancode, SDL_GetScancodeName(scancode));
   400                 }
   401             }
   402         }
   403     }
   404 
   405     X11_UpdateKeymap(_this);
   406 
   407     SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
   408 
   409 #ifdef SDL_USE_IME
   410     SDL_IME_Init();
   411 #endif
   412 
   413     return 0;
   414 }
   415 
   416 void
   417 X11_UpdateKeymap(_THIS)
   418 {
   419     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   420     int i;
   421     SDL_Scancode scancode;
   422     SDL_Keycode keymap[SDL_NUM_SCANCODES];
   423     unsigned char group = 0;
   424 
   425     SDL_GetDefaultKeymap(keymap);
   426 
   427 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
   428     if (data->xkb) {
   429         XkbStateRec state;
   430         X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb);
   431 
   432         if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
   433             group = state.group;
   434         }
   435     }
   436 #endif
   437 
   438 
   439     for (i = 0; i < SDL_arraysize(data->key_layout); i++) {
   440         Uint32 key;
   441 
   442         /* Make sure this is a valid scancode */
   443         scancode = data->key_layout[i];
   444         if (scancode == SDL_SCANCODE_UNKNOWN) {
   445             continue;
   446         }
   447 
   448         /* See if there is a UCS keycode for this scancode */
   449         key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group);
   450         if (key) {
   451             keymap[scancode] = key;
   452         } else {
   453             SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i);
   454 
   455             switch (keyScancode) {
   456                 case SDL_SCANCODE_RETURN:
   457                     keymap[scancode] = SDLK_RETURN;
   458                     break;
   459                 case SDL_SCANCODE_ESCAPE:
   460                     keymap[scancode] = SDLK_ESCAPE;
   461                     break;
   462                 case SDL_SCANCODE_BACKSPACE:
   463                     keymap[scancode] = SDLK_BACKSPACE;
   464                     break;
   465                 case SDL_SCANCODE_TAB:
   466                     keymap[scancode] = SDLK_TAB;
   467                     break;
   468                 case SDL_SCANCODE_DELETE:
   469                     keymap[scancode] = SDLK_DELETE;
   470                     break;
   471                 default:
   472                     keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(keyScancode);
   473                     break;
   474             }
   475         }
   476     }
   477     SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
   478 }
   479 
   480 void
   481 X11_QuitKeyboard(_THIS)
   482 {
   483     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   484 
   485 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
   486     if (data->xkb) {
   487         X11_XkbFreeClientMap(data->xkb, 0, True);
   488         data->xkb = NULL;
   489     }
   490 #endif
   491 
   492 #ifdef SDL_USE_IME
   493     SDL_IME_Quit();
   494 #endif
   495 }
   496 
   497 static void
   498 X11_ResetXIM(_THIS)
   499 {
   500 #ifdef X_HAVE_UTF8_STRING
   501     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   502     int i;
   503 
   504     if (videodata && videodata->windowlist) {
   505         for (i = 0; i < videodata->numwindows; ++i) {
   506             SDL_WindowData *data = videodata->windowlist[i];
   507             if (data && data->ic) {
   508                 /* Clear any partially entered dead keys */
   509                 char *contents = X11_Xutf8ResetIC(data->ic);
   510                 if (contents) {
   511                     X11_XFree(contents);
   512                 }
   513             }
   514         }
   515     }
   516 #endif
   517 }
   518 
   519 void
   520 X11_StartTextInput(_THIS)
   521 {
   522     X11_ResetXIM(_this);
   523 }
   524 
   525 void
   526 X11_StopTextInput(_THIS)
   527 {
   528     X11_ResetXIM(_this);
   529 #ifdef SDL_USE_IME
   530     SDL_IME_Reset();
   531 #endif
   532 }
   533 
   534 void
   535 X11_SetTextInputRect(_THIS, SDL_Rect *rect)
   536 {
   537     if (!rect) {
   538         SDL_InvalidParamError("rect");
   539         return;
   540     }
   541        
   542 #ifdef SDL_USE_IME
   543     SDL_IME_UpdateTextRect(rect);
   544 #endif
   545 }
   546 
   547 #endif /* SDL_VIDEO_DRIVER_X11 */
   548 
   549 /* vi: set ts=4 sw=4 expandtab: */