Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / x11 / SDL_x11keyboard.c
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
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     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
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
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
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
517 X11_StartTextInput(_THIS)
518 {
519     X11_ResetXIM(_this);
520 }
521
522 void
523 X11_StopTextInput(_THIS)
524 {
525     X11_ResetXIM(_this);
526 #ifdef SDL_USE_IME
527     SDL_IME_Reset();
528 #endif
529 }
530
531 void
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: */