d667c7b22ae3ce920b82ab8d4f549ae023ae971f
[platform/upstream/SDL.git] / src / video / x11 / SDL_x11keyboard.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2018 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 report the following to the SDL forums/mailing list (https://discourse.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_XkbFreeKeyboard(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: */