2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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 },
270 XKeyboardState values = { .global_auto_repeat = AutoRepeatModeOff };
272 X11_XGetKeyboardControl(data->display, &values);
273 if (values.global_auto_repeat != AutoRepeatModeOn)
274 X11_XAutoRepeatOn(data->display);
276 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
278 int xkb_major = XkbMajorVersion;
279 int xkb_minor = XkbMinorVersion;
281 if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) {
282 data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
285 /* This will remove KeyRelease events for held keys */
286 X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
290 /* Open a connection to the X input manager */
291 #ifdef X_HAVE_UTF8_STRING
292 if (SDL_X11_HAVE_UTF8) {
293 /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that
294 Compose keys will work correctly. */
295 char *prev_locale = setlocale(LC_ALL, NULL);
296 char *prev_xmods = X11_XSetLocaleModifiers(NULL);
297 const char *new_xmods = "";
298 const char *env_xmods = SDL_getenv("XMODIFIERS");
299 SDL_bool has_dbus_ime_support = SDL_FALSE;
302 prev_locale = SDL_strdup(prev_locale);
306 prev_xmods = SDL_strdup(prev_xmods);
309 /* IBus resends some key events that were filtered by XFilterEvents
310 when it is used via XIM which causes issues. Prevent this by forcing
311 @im=none if XMODIFIERS contains @im=ibus. IBus can still be used via
312 the DBus implementation, which also has support for pre-editing. */
313 if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) {
314 has_dbus_ime_support = SDL_TRUE;
316 if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) {
317 has_dbus_ime_support = SDL_TRUE;
319 if (has_dbus_ime_support || !xkb_repeat) {
320 new_xmods = "@im=none";
323 setlocale(LC_ALL, "");
324 X11_XSetLocaleModifiers(new_xmods);
326 data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname);
328 /* Reset the locale + X locale modifiers back to how they were,
329 locale first because the X locale modifiers depend on it. */
330 setlocale(LC_ALL, prev_locale);
331 X11_XSetLocaleModifiers(prev_xmods);
334 SDL_free(prev_locale);
338 SDL_free(prev_xmods);
342 /* Try to determine which scancodes are being used based on fingerprint */
343 best_distance = SDL_arraysize(fingerprint) + 1;
345 X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode);
346 for (i = 0; i < SDL_arraysize(fingerprint); ++i) {
347 fingerprint[i].value =
348 X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) -
351 for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
352 /* Make sure the scancode set isn't too big */
353 if ((max_keycode - min_keycode + 1) <= scancode_set[i].table_size) {
357 for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
358 if (fingerprint[j].value < 0
359 || fingerprint[j].value >= scancode_set[i].table_size) {
361 } else if (scancode_set[i].table[fingerprint[j].value] != fingerprint[j].scancode) {
365 if (distance < best_distance) {
366 best_distance = distance;
370 if (best_index >= 0 && best_distance <= 2) {
371 #ifdef DEBUG_KEYBOARD
372 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);
374 SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table,
375 sizeof(SDL_Scancode) * scancode_set[best_index].table_size);
377 SDL_Keycode keymap[SDL_NUM_SCANCODES];
380 ("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):\n");
382 /* Determine key_layout - only works on US QWERTY layout */
383 SDL_GetDefaultKeymap(keymap);
384 for (i = min_keycode; i <= max_keycode; ++i) {
386 sym = X11_KeyCodeToSym(_this, (KeyCode) i, 0);
387 if (sym != NoSymbol) {
388 SDL_Scancode scancode;
389 printf("code = %d, sym = 0x%X (%s) ", i - min_keycode,
390 (unsigned int) sym, X11_XKeysymToString(sym));
391 scancode = X11_KeyCodeToSDLScancode(_this, i);
392 data->key_layout[i] = scancode;
393 if (scancode == SDL_SCANCODE_UNKNOWN) {
394 printf("scancode not found\n");
396 printf("scancode = %d (%s)\n", scancode, SDL_GetScancodeName(scancode));
402 X11_UpdateKeymap(_this);
404 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
414 X11_UpdateKeymap(_THIS)
416 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
418 SDL_Scancode scancode;
419 SDL_Keycode keymap[SDL_NUM_SCANCODES];
420 unsigned char group = 0;
422 SDL_GetDefaultKeymap(keymap);
424 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
427 X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb);
429 if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
436 for (i = 0; i < SDL_arraysize(data->key_layout); i++) {
439 /* Make sure this is a valid scancode */
440 scancode = data->key_layout[i];
441 if (scancode == SDL_SCANCODE_UNKNOWN) {
445 /* See if there is a UCS keycode for this scancode */
446 key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group);
448 keymap[scancode] = key;
450 SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i);
452 switch (keyScancode) {
453 case SDL_SCANCODE_RETURN:
454 keymap[scancode] = SDLK_RETURN;
456 case SDL_SCANCODE_ESCAPE:
457 keymap[scancode] = SDLK_ESCAPE;
459 case SDL_SCANCODE_BACKSPACE:
460 keymap[scancode] = SDLK_BACKSPACE;
462 case SDL_SCANCODE_TAB:
463 keymap[scancode] = SDLK_TAB;
465 case SDL_SCANCODE_DELETE:
466 keymap[scancode] = SDLK_DELETE;
469 keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(keyScancode);
474 SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
478 X11_QuitKeyboard(_THIS)
480 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
482 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
484 X11_XkbFreeKeyboard(data->xkb, 0, True);
497 #ifdef X_HAVE_UTF8_STRING
498 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
501 if (videodata && videodata->windowlist) {
502 for (i = 0; i < videodata->numwindows; ++i) {
503 SDL_WindowData *data = videodata->windowlist[i];
504 if (data && data->ic) {
505 /* Clear any partially entered dead keys */
506 char *contents = X11_Xutf8ResetIC(data->ic);
517 X11_StartTextInput(_THIS)
523 X11_StopTextInput(_THIS)
532 X11_SetTextInputRect(_THIS, SDL_Rect *rect)
535 SDL_InvalidParamError("rect");
540 SDL_IME_UpdateTextRect(rect);
544 #endif /* SDL_VIDEO_DRIVER_X11 */
546 /* vi: set ts=4 sw=4 expandtab: */