2 Simple DirectMedia Layer
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_X11
25 #include "SDL_x11video.h"
27 #include "../../events/SDL_keyboard_c.h"
28 #include "../../events/scancodes_darwin.h"
29 #include "../../events/scancodes_xfree86.h"
31 #include <X11/keysym.h>
32 #include <X11/XKBlib.h>
34 #include "imKStoUCS.h"
36 #ifdef X_HAVE_UTF8_STRING
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 },
155 SDL_Scancode const *table;
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) },
165 /* This function only works for keyboards in US QWERTY layout */
167 X11_KeyCodeToSDLScancode(_THIS, KeyCode keycode)
172 keysym = X11_KeyCodeToSym(_this, keycode, 0);
173 if (keysym == NoSymbol) {
174 return SDL_SCANCODE_UNKNOWN;
177 if (keysym >= XK_a && keysym <= XK_z) {
178 return SDL_SCANCODE_A + (keysym - XK_a);
180 if (keysym >= XK_A && keysym <= XK_Z) {
181 return SDL_SCANCODE_A + (keysym - XK_A);
184 if (keysym == XK_0) {
185 return SDL_SCANCODE_0;
187 if (keysym >= XK_1 && keysym <= XK_9) {
188 return SDL_SCANCODE_1 + (keysym - XK_1);
191 for (i = 0; i < SDL_arraysize(KeySymToSDLScancode); ++i) {
192 if (keysym == KeySymToSDLScancode[i].keysym) {
193 return KeySymToSDLScancode[i].scancode;
196 return SDL_SCANCODE_UNKNOWN;
200 X11_KeyCodeToUcs4(_THIS, KeyCode keycode, unsigned char group)
202 KeySym keysym = X11_KeyCodeToSym(_this, keycode, group);
204 if (keysym == NoSymbol) {
208 return X11_KeySymToUcs4(keysym);
212 X11_KeyCodeToSym(_THIS, KeyCode keycode, unsigned char group)
214 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
217 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
219 int num_groups = XkbKeyNumGroups(data->xkb, keycode);
220 unsigned char info = XkbKeyGroupInfo(data->xkb, keycode);
222 if (num_groups && group >= num_groups) {
224 int action = XkbOutOfRangeGroupAction(info);
226 if (action == XkbRedirectIntoRange) {
227 if ((group = XkbOutOfRangeGroupNumber(info)) >= num_groups) {
230 } else if (action == XkbClampIntoRange) {
231 group = num_groups - 1;
236 keysym = X11_XkbKeycodeToKeysym(data->display, keycode, group, 0);
238 keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
241 keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
248 X11_InitKeyboard(_THIS)
250 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
253 int min_keycode, max_keycode;
255 SDL_Scancode scancode;
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 },
271 X11_XAutoRepeatOn(data->display);
273 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
275 int xkb_major = XkbMajorVersion;
276 int xkb_minor = XkbMinorVersion;
278 if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) {
279 data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
282 /* This will remove KeyRelease events for held keys */
283 X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
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");
298 SDL_bool has_dbus_ime_support = SDL_FALSE;
301 prev_locale = SDL_strdup(prev_locale);
305 prev_xmods = SDL_strdup(prev_xmods);
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;
317 #ifdef HAVE_FCITX_FRONTEND_H
318 if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) {
319 has_dbus_ime_support = SDL_TRUE;
322 if (has_dbus_ime_support || !xkb_repeat) {
323 new_xmods = "@im=none";
326 setlocale(LC_ALL, "");
327 X11_XSetLocaleModifiers(new_xmods);
329 data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname);
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);
337 SDL_free(prev_locale);
341 SDL_free(prev_xmods);
345 /* Try to determine which scancodes are being used based on fingerprint */
346 best_distance = SDL_arraysize(fingerprint) + 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) -
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) {
360 for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
361 if (fingerprint[j].value < 0
362 || fingerprint[j].value >= scancode_set[i].table_size) {
364 } else if (scancode_set[i].table[fingerprint[j].value] != fingerprint[j].scancode) {
368 if (distance < best_distance) {
369 best_distance = distance;
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);
377 SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table,
378 sizeof(SDL_Scancode) * scancode_set[best_index].table_size);
380 SDL_Keycode keymap[SDL_NUM_SCANCODES];
383 ("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):\n");
385 /* Determine key_layout - only works on US QWERTY layout */
386 SDL_GetDefaultKeymap(keymap);
387 for (i = min_keycode; i <= max_keycode; ++i) {
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");
399 printf("scancode = %d (%s)\n", scancode, SDL_GetScancodeName(scancode));
405 X11_UpdateKeymap(_this);
407 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
417 X11_UpdateKeymap(_THIS)
419 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
421 SDL_Scancode scancode;
422 SDL_Keycode keymap[SDL_NUM_SCANCODES];
423 unsigned char group = 0;
425 SDL_GetDefaultKeymap(keymap);
427 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
430 X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb);
432 if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
439 for (i = 0; i < SDL_arraysize(data->key_layout); i++) {
442 /* Make sure this is a valid scancode */
443 scancode = data->key_layout[i];
444 if (scancode == SDL_SCANCODE_UNKNOWN) {
448 /* See if there is a UCS keycode for this scancode */
449 key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group);
451 keymap[scancode] = key;
453 SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i);
455 switch (keyScancode) {
456 case SDL_SCANCODE_RETURN:
457 keymap[scancode] = SDLK_RETURN;
459 case SDL_SCANCODE_ESCAPE:
460 keymap[scancode] = SDLK_ESCAPE;
462 case SDL_SCANCODE_BACKSPACE:
463 keymap[scancode] = SDLK_BACKSPACE;
465 case SDL_SCANCODE_TAB:
466 keymap[scancode] = SDLK_TAB;
468 case SDL_SCANCODE_DELETE:
469 keymap[scancode] = SDLK_DELETE;
472 keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(keyScancode);
477 SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
481 X11_QuitKeyboard(_THIS)
483 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
485 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
487 X11_XkbFreeKeyboard(data->xkb, 0, True);
500 #ifdef X_HAVE_UTF8_STRING
501 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
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);
520 X11_StartTextInput(_THIS)
526 X11_StopTextInput(_THIS)
535 X11_SetTextInputRect(_THIS, SDL_Rect *rect)
538 SDL_InvalidParamError("rect");
543 SDL_IME_UpdateTextRect(rect);
547 #endif /* SDL_VIDEO_DRIVER_X11 */
549 /* vi: set ts=4 sw=4 expandtab: */