1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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
X11_KeyCodeToSDLScancode(_THIS,KeyCode keycode)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
X11_KeyCodeToUcs4(_THIS,KeyCode keycode,unsigned char group)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
X11_KeyCodeToSym(_THIS,KeyCode keycode,unsigned char group)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
X11_InitKeyboard(_THIS)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     XKeyboardState values = { .global_auto_repeat = AutoRepeatModeOff };
271 
272     X11_XGetKeyboardControl(data->display, &values);
273     if (values.global_auto_repeat != AutoRepeatModeOn)
274         X11_XAutoRepeatOn(data->display);
275 
276 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
277     {
278         int xkb_major = XkbMajorVersion;
279         int xkb_minor = XkbMinorVersion;
280 
281         if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) {
282             data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
283         }
284 
285         /* This will remove KeyRelease events for held keys */
286         X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
287     }
288 #endif
289 
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;
300 
301         if (prev_locale) {
302             prev_locale = SDL_strdup(prev_locale);
303         }
304 
305         if (prev_xmods) {
306             prev_xmods = SDL_strdup(prev_xmods);
307         }
308 
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;
315         }
316         if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) {
317             has_dbus_ime_support = SDL_TRUE;
318         }
319         if (has_dbus_ime_support || !xkb_repeat) {
320             new_xmods = "@im=none";
321         }
322 
323         setlocale(LC_ALL, "");
324         X11_XSetLocaleModifiers(new_xmods);
325 
326         data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname);
327 
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);
332 
333         if (prev_locale) {
334             SDL_free(prev_locale);
335         }
336 
337         if (prev_xmods) {
338             SDL_free(prev_xmods);
339         }
340     }
341 #endif
342     /* Try to determine which scancodes are being used based on fingerprint */
343     best_distance = SDL_arraysize(fingerprint) + 1;
344     best_index = -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) -
349             min_keycode;
350     }
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) {
354             continue;
355         }
356         distance = 0;
357         for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
358             if (fingerprint[j].value < 0
359                 || fingerprint[j].value >= scancode_set[i].table_size) {
360                 distance += 1;
361             } else if (scancode_set[i].table[fingerprint[j].value] != fingerprint[j].scancode) {
362                 distance += 1;
363             }
364         }
365         if (distance < best_distance) {
366             best_distance = distance;
367             best_index = i;
368         }
369     }
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);
373 #endif
374         SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table,
375                    sizeof(SDL_Scancode) * scancode_set[best_index].table_size);
376     } else {
377         SDL_Keycode keymap[SDL_NUM_SCANCODES];
378 
379         printf
380             ("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):\n");
381 
382         /* Determine key_layout - only works on US QWERTY layout */
383         SDL_GetDefaultKeymap(keymap);
384         for (i = min_keycode; i <= max_keycode; ++i) {
385             KeySym sym;
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");
395                 } else {
396                     printf("scancode = %d (%s)\n", scancode, SDL_GetScancodeName(scancode));
397                 }
398             }
399         }
400     }
401 
402     X11_UpdateKeymap(_this);
403 
404     SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
405 
406 #ifdef SDL_USE_IME
407     SDL_IME_Init();
408 #endif
409 
410     return 0;
411 }
412 
413 void
X11_UpdateKeymap(_THIS)414 X11_UpdateKeymap(_THIS)
415 {
416     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
417     int i;
418     SDL_Scancode scancode;
419     SDL_Keycode keymap[SDL_NUM_SCANCODES];
420     unsigned char group = 0;
421 
422     SDL_GetDefaultKeymap(keymap);
423 
424 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
425     if (data->xkb) {
426         XkbStateRec state;
427         X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb);
428 
429         if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
430             group = state.group;
431         }
432     }
433 #endif
434 
435 
436     for (i = 0; i < SDL_arraysize(data->key_layout); i++) {
437         Uint32 key;
438 
439         /* Make sure this is a valid scancode */
440         scancode = data->key_layout[i];
441         if (scancode == SDL_SCANCODE_UNKNOWN) {
442             continue;
443         }
444 
445         /* See if there is a UCS keycode for this scancode */
446         key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group);
447         if (key) {
448             keymap[scancode] = key;
449         } else {
450             SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i);
451 
452             switch (keyScancode) {
453                 case SDL_SCANCODE_RETURN:
454                     keymap[scancode] = SDLK_RETURN;
455                     break;
456                 case SDL_SCANCODE_ESCAPE:
457                     keymap[scancode] = SDLK_ESCAPE;
458                     break;
459                 case SDL_SCANCODE_BACKSPACE:
460                     keymap[scancode] = SDLK_BACKSPACE;
461                     break;
462                 case SDL_SCANCODE_TAB:
463                     keymap[scancode] = SDLK_TAB;
464                     break;
465                 case SDL_SCANCODE_DELETE:
466                     keymap[scancode] = SDLK_DELETE;
467                     break;
468                 default:
469                     keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(keyScancode);
470                     break;
471             }
472         }
473     }
474     SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
475 }
476 
477 void
X11_QuitKeyboard(_THIS)478 X11_QuitKeyboard(_THIS)
479 {
480     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
481 
482 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
483     if (data->xkb) {
484         X11_XkbFreeKeyboard(data->xkb, 0, True);
485         data->xkb = NULL;
486     }
487 #endif
488 
489 #ifdef SDL_USE_IME
490     SDL_IME_Quit();
491 #endif
492 }
493 
494 static void
X11_ResetXIM(_THIS)495 X11_ResetXIM(_THIS)
496 {
497 #ifdef X_HAVE_UTF8_STRING
498     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
499     int i;
500 
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);
507                 if (contents) {
508                     X11_XFree(contents);
509                 }
510             }
511         }
512     }
513 #endif
514 }
515 
516 void
X11_StartTextInput(_THIS)517 X11_StartTextInput(_THIS)
518 {
519     X11_ResetXIM(_this);
520 }
521 
522 void
X11_StopTextInput(_THIS)523 X11_StopTextInput(_THIS)
524 {
525     X11_ResetXIM(_this);
526 #ifdef SDL_USE_IME
527     SDL_IME_Reset();
528 #endif
529 }
530 
531 void
X11_SetTextInputRect(_THIS,SDL_Rect * rect)532 X11_SetTextInputRect(_THIS, SDL_Rect *rect)
533 {
534     if (!rect) {
535         SDL_InvalidParamError("rect");
536         return;
537     }
538 
539 #ifdef SDL_USE_IME
540     SDL_IME_UpdateTextRect(rect);
541 #endif
542 }
543 
544 #endif /* SDL_VIDEO_DRIVER_X11 */
545 
546 /* vi: set ts=4 sw=4 expandtab: */
547