• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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 /* *INDENT-OFF* */
37 static const struct {
38     KeySym keysym;
39     SDL_Scancode scancode;
40 } KeySymToSDLScancode[] = {
41     { XK_Return, SDL_SCANCODE_RETURN },
42     { XK_Escape, SDL_SCANCODE_ESCAPE },
43     { XK_BackSpace, SDL_SCANCODE_BACKSPACE },
44     { XK_Tab, SDL_SCANCODE_TAB },
45     { XK_Caps_Lock, SDL_SCANCODE_CAPSLOCK },
46     { XK_F1, SDL_SCANCODE_F1 },
47     { XK_F2, SDL_SCANCODE_F2 },
48     { XK_F3, SDL_SCANCODE_F3 },
49     { XK_F4, SDL_SCANCODE_F4 },
50     { XK_F5, SDL_SCANCODE_F5 },
51     { XK_F6, SDL_SCANCODE_F6 },
52     { XK_F7, SDL_SCANCODE_F7 },
53     { XK_F8, SDL_SCANCODE_F8 },
54     { XK_F9, SDL_SCANCODE_F9 },
55     { XK_F10, SDL_SCANCODE_F10 },
56     { XK_F11, SDL_SCANCODE_F11 },
57     { XK_F12, SDL_SCANCODE_F12 },
58     { XK_Print, SDL_SCANCODE_PRINTSCREEN },
59     { XK_Scroll_Lock, SDL_SCANCODE_SCROLLLOCK },
60     { XK_Pause, SDL_SCANCODE_PAUSE },
61     { XK_Insert, SDL_SCANCODE_INSERT },
62     { XK_Home, SDL_SCANCODE_HOME },
63     { XK_Prior, SDL_SCANCODE_PAGEUP },
64     { XK_Delete, SDL_SCANCODE_DELETE },
65     { XK_End, SDL_SCANCODE_END },
66     { XK_Next, SDL_SCANCODE_PAGEDOWN },
67     { XK_Right, SDL_SCANCODE_RIGHT },
68     { XK_Left, SDL_SCANCODE_LEFT },
69     { XK_Down, SDL_SCANCODE_DOWN },
70     { XK_Up, SDL_SCANCODE_UP },
71     { XK_Num_Lock, SDL_SCANCODE_NUMLOCKCLEAR },
72     { XK_KP_Divide, SDL_SCANCODE_KP_DIVIDE },
73     { XK_KP_Multiply, SDL_SCANCODE_KP_MULTIPLY },
74     { XK_KP_Subtract, SDL_SCANCODE_KP_MINUS },
75     { XK_KP_Add, SDL_SCANCODE_KP_PLUS },
76     { XK_KP_Enter, SDL_SCANCODE_KP_ENTER },
77     { XK_KP_Delete, SDL_SCANCODE_KP_PERIOD },
78     { XK_KP_End, SDL_SCANCODE_KP_1 },
79     { XK_KP_Down, SDL_SCANCODE_KP_2 },
80     { XK_KP_Next, SDL_SCANCODE_KP_3 },
81     { XK_KP_Left, SDL_SCANCODE_KP_4 },
82     { XK_KP_Begin, SDL_SCANCODE_KP_5 },
83     { XK_KP_Right, SDL_SCANCODE_KP_6 },
84     { XK_KP_Home, SDL_SCANCODE_KP_7 },
85     { XK_KP_Up, SDL_SCANCODE_KP_8 },
86     { XK_KP_Prior, SDL_SCANCODE_KP_9 },
87     { XK_KP_Insert, SDL_SCANCODE_KP_0 },
88     { XK_KP_Decimal, SDL_SCANCODE_KP_PERIOD },
89     { XK_KP_1, SDL_SCANCODE_KP_1 },
90     { XK_KP_2, SDL_SCANCODE_KP_2 },
91     { XK_KP_3, SDL_SCANCODE_KP_3 },
92     { XK_KP_4, SDL_SCANCODE_KP_4 },
93     { XK_KP_5, SDL_SCANCODE_KP_5 },
94     { XK_KP_6, SDL_SCANCODE_KP_6 },
95     { XK_KP_7, SDL_SCANCODE_KP_7 },
96     { XK_KP_8, SDL_SCANCODE_KP_8 },
97     { XK_KP_9, SDL_SCANCODE_KP_9 },
98     { XK_KP_0, SDL_SCANCODE_KP_0 },
99     { XK_KP_Decimal, SDL_SCANCODE_KP_PERIOD },
100     { XK_Hyper_R, SDL_SCANCODE_APPLICATION },
101     { XK_KP_Equal, SDL_SCANCODE_KP_EQUALS },
102     { XK_F13, SDL_SCANCODE_F13 },
103     { XK_F14, SDL_SCANCODE_F14 },
104     { XK_F15, SDL_SCANCODE_F15 },
105     { XK_F16, SDL_SCANCODE_F16 },
106     { XK_F17, SDL_SCANCODE_F17 },
107     { XK_F18, SDL_SCANCODE_F18 },
108     { XK_F19, SDL_SCANCODE_F19 },
109     { XK_F20, SDL_SCANCODE_F20 },
110     { XK_F21, SDL_SCANCODE_F21 },
111     { XK_F22, SDL_SCANCODE_F22 },
112     { XK_F23, SDL_SCANCODE_F23 },
113     { XK_F24, SDL_SCANCODE_F24 },
114     { XK_Execute, SDL_SCANCODE_EXECUTE },
115     { XK_Help, SDL_SCANCODE_HELP },
116     { XK_Menu, SDL_SCANCODE_MENU },
117     { XK_Select, SDL_SCANCODE_SELECT },
118     { XK_Cancel, SDL_SCANCODE_STOP },
119     { XK_Redo, SDL_SCANCODE_AGAIN },
120     { XK_Undo, SDL_SCANCODE_UNDO },
121     { XK_Find, SDL_SCANCODE_FIND },
122     { XK_KP_Separator, SDL_SCANCODE_KP_COMMA },
123     { XK_Sys_Req, SDL_SCANCODE_SYSREQ },
124     { XK_Control_L, SDL_SCANCODE_LCTRL },
125     { XK_Shift_L, SDL_SCANCODE_LSHIFT },
126     { XK_Alt_L, SDL_SCANCODE_LALT },
127     { XK_Meta_L, SDL_SCANCODE_LGUI },
128     { XK_Super_L, SDL_SCANCODE_LGUI },
129     { XK_Control_R, SDL_SCANCODE_RCTRL },
130     { XK_Shift_R, SDL_SCANCODE_RSHIFT },
131     { XK_Alt_R, SDL_SCANCODE_RALT },
132     { XK_ISO_Level3_Shift, SDL_SCANCODE_RALT },
133     { XK_Meta_R, SDL_SCANCODE_RGUI },
134     { XK_Super_R, SDL_SCANCODE_RGUI },
135     { XK_Mode_switch, SDL_SCANCODE_MODE },
136     { XK_period, SDL_SCANCODE_PERIOD },
137     { XK_comma, SDL_SCANCODE_COMMA },
138     { XK_slash, SDL_SCANCODE_SLASH },
139     { XK_backslash, SDL_SCANCODE_BACKSLASH },
140     { XK_minus, SDL_SCANCODE_MINUS },
141     { XK_equal, SDL_SCANCODE_EQUALS },
142     { XK_space, SDL_SCANCODE_SPACE },
143     { XK_grave, SDL_SCANCODE_GRAVE },
144     { XK_apostrophe, SDL_SCANCODE_APOSTROPHE },
145     { XK_bracketleft, SDL_SCANCODE_LEFTBRACKET },
146     { XK_bracketright, SDL_SCANCODE_RIGHTBRACKET },
147 };
148 
149 static const struct
150 {
151     SDL_Scancode const *table;
152     int table_size;
153 } scancode_set[] = {
154     { darwin_scancode_table, SDL_arraysize(darwin_scancode_table) },
155     { xfree86_scancode_table, SDL_arraysize(xfree86_scancode_table) },
156     { xfree86_scancode_table2, SDL_arraysize(xfree86_scancode_table2) },
157     { xvnc_scancode_table, SDL_arraysize(xvnc_scancode_table) },
158 };
159 /* *INDENT-OFF* */
160 
161 /* This function only works for keyboards in US QWERTY layout */
162 static SDL_Scancode
X11_KeyCodeToSDLScancode(_THIS,KeyCode keycode)163 X11_KeyCodeToSDLScancode(_THIS, KeyCode keycode)
164 {
165     KeySym keysym;
166     int i;
167 
168     keysym = X11_KeyCodeToSym(_this, keycode, 0);
169     if (keysym == NoSymbol) {
170         return SDL_SCANCODE_UNKNOWN;
171     }
172 
173     if (keysym >= XK_a && keysym <= XK_z) {
174         return SDL_SCANCODE_A + (keysym - XK_a);
175     }
176     if (keysym >= XK_A && keysym <= XK_Z) {
177         return SDL_SCANCODE_A + (keysym - XK_A);
178     }
179 
180     if (keysym == XK_0) {
181         return SDL_SCANCODE_0;
182     }
183     if (keysym >= XK_1 && keysym <= XK_9) {
184         return SDL_SCANCODE_1 + (keysym - XK_1);
185     }
186 
187     for (i = 0; i < SDL_arraysize(KeySymToSDLScancode); ++i) {
188         if (keysym == KeySymToSDLScancode[i].keysym) {
189             return KeySymToSDLScancode[i].scancode;
190         }
191     }
192     return SDL_SCANCODE_UNKNOWN;
193 }
194 
195 static Uint32
X11_KeyCodeToUcs4(_THIS,KeyCode keycode,unsigned char group)196 X11_KeyCodeToUcs4(_THIS, KeyCode keycode, unsigned char group)
197 {
198     KeySym keysym = X11_KeyCodeToSym(_this, keycode, group);
199 
200     if (keysym == NoSymbol) {
201         return 0;
202     }
203 
204     return X11_KeySymToUcs4(keysym);
205 }
206 
207 KeySym
X11_KeyCodeToSym(_THIS,KeyCode keycode,unsigned char group)208 X11_KeyCodeToSym(_THIS, KeyCode keycode, unsigned char group)
209 {
210     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
211     KeySym keysym;
212 
213 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
214     if (data->xkb) {
215         int num_groups     = XkbKeyNumGroups(data->xkb, keycode);
216         unsigned char info = XkbKeyGroupInfo(data->xkb, keycode);
217 
218         if (num_groups && group >= num_groups) {
219 
220             int action = XkbOutOfRangeGroupAction(info);
221 
222             if (action == XkbRedirectIntoRange) {
223                 if ((group = XkbOutOfRangeGroupNumber(info)) >= num_groups) {
224                     group = 0;
225                 }
226             } else if (action == XkbClampIntoRange) {
227                 group = num_groups - 1;
228             } else {
229                 group %= num_groups;
230             }
231         }
232         keysym = X11_XkbKeycodeToKeysym(data->display, keycode, group, 0);
233     } else {
234         keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
235     }
236 #else
237     keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
238 #endif
239 
240     return keysym;
241 }
242 
243 int
X11_InitKeyboard(_THIS)244 X11_InitKeyboard(_THIS)
245 {
246     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
247     int i = 0;
248     int j = 0;
249     int min_keycode, max_keycode;
250     struct {
251         SDL_Scancode scancode;
252         KeySym keysym;
253         int value;
254     } fingerprint[] = {
255         { SDL_SCANCODE_HOME, XK_Home, 0 },
256         { SDL_SCANCODE_PAGEUP, XK_Prior, 0 },
257         { SDL_SCANCODE_UP, XK_Up, 0 },
258         { SDL_SCANCODE_LEFT, XK_Left, 0 },
259         { SDL_SCANCODE_DELETE, XK_Delete, 0 },
260         { SDL_SCANCODE_KP_ENTER, XK_KP_Enter, 0 },
261     };
262     int best_distance;
263     int best_index;
264     int distance;
265 
266     X11_XAutoRepeatOn(data->display);
267 
268 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
269     {
270 	    int xkb_major = XkbMajorVersion;
271 	    int xkb_minor = XkbMinorVersion;
272 	    if (X11_XkbQueryExtension(data->display, NULL, NULL, NULL, &xkb_major, &xkb_minor)) {
273 	        data->xkb = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
274 	    }
275 	}
276 #endif
277 
278     /* Try to determine which scancodes are being used based on fingerprint */
279     best_distance = SDL_arraysize(fingerprint) + 1;
280     best_index = -1;
281     X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode);
282     for (i = 0; i < SDL_arraysize(fingerprint); ++i) {
283         fingerprint[i].value =
284             X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) -
285             min_keycode;
286     }
287     for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
288         /* Make sure the scancode set isn't too big */
289         if ((max_keycode - min_keycode + 1) <= scancode_set[i].table_size) {
290             continue;
291         }
292         distance = 0;
293         for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
294             if (fingerprint[j].value < 0
295                 || fingerprint[j].value >= scancode_set[i].table_size) {
296                 distance += 1;
297             } else if (scancode_set[i].table[fingerprint[j].value] != fingerprint[j].scancode) {
298                 distance += 1;
299             }
300         }
301         if (distance < best_distance) {
302             best_distance = distance;
303             best_index = i;
304         }
305     }
306     if (best_index >= 0 && best_distance <= 2) {
307 #ifdef DEBUG_KEYBOARD
308         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);
309 #endif
310         SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table,
311                    sizeof(SDL_Scancode) * scancode_set[best_index].table_size);
312     } else {
313         SDL_Keycode keymap[SDL_NUM_SCANCODES];
314 
315         printf
316             ("Keyboard layout unknown, please send the following to the SDL mailing list (sdl@libsdl.org):\n");
317 
318         /* Determine key_layout - only works on US QWERTY layout */
319         SDL_GetDefaultKeymap(keymap);
320         for (i = min_keycode; i <= max_keycode; ++i) {
321             KeySym sym;
322             sym = X11_KeyCodeToSym(_this, (KeyCode) i, 0);
323             if (sym != NoSymbol) {
324                 SDL_Scancode scancode;
325                 printf("code = %d, sym = 0x%X (%s) ", i - min_keycode,
326                        (unsigned int) sym, X11_XKeysymToString(sym));
327                 scancode = X11_KeyCodeToSDLScancode(_this, i);
328                 data->key_layout[i] = scancode;
329                 if (scancode == SDL_SCANCODE_UNKNOWN) {
330                     printf("scancode not found\n");
331                 } else {
332                     printf("scancode = %d (%s)\n", scancode, SDL_GetScancodeName(scancode));
333                 }
334             }
335         }
336     }
337 
338     X11_UpdateKeymap(_this);
339 
340     SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
341 
342 #ifdef SDL_USE_IME
343     SDL_IME_Init();
344 #endif
345 
346     return 0;
347 }
348 
349 void
X11_UpdateKeymap(_THIS)350 X11_UpdateKeymap(_THIS)
351 {
352     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
353     int i;
354     SDL_Scancode scancode;
355     SDL_Keycode keymap[SDL_NUM_SCANCODES];
356     unsigned char group = 0;
357 
358     SDL_GetDefaultKeymap(keymap);
359 
360 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
361     if (data->xkb) {
362         XkbStateRec state;
363         X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb);
364 
365         if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
366             group = state.group;
367         }
368     }
369 #endif
370 
371 
372     for (i = 0; i < SDL_arraysize(data->key_layout); i++) {
373         Uint32 key;
374 
375         /* Make sure this is a valid scancode */
376         scancode = data->key_layout[i];
377         if (scancode == SDL_SCANCODE_UNKNOWN) {
378             continue;
379         }
380 
381         /* See if there is a UCS keycode for this scancode */
382         key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group);
383         if (key) {
384             keymap[scancode] = key;
385         } else {
386             SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i);
387 
388             switch (keyScancode) {
389                 case SDL_SCANCODE_RETURN:
390                     keymap[scancode] = SDLK_RETURN;
391                     break;
392                 case SDL_SCANCODE_ESCAPE:
393                     keymap[scancode] = SDLK_ESCAPE;
394                     break;
395                 case SDL_SCANCODE_BACKSPACE:
396                     keymap[scancode] = SDLK_BACKSPACE;
397                     break;
398                 case SDL_SCANCODE_TAB:
399                     keymap[scancode] = SDLK_TAB;
400                     break;
401                 case SDL_SCANCODE_DELETE:
402                     keymap[scancode] = SDLK_DELETE;
403                     break;
404                 default:
405                     keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(keyScancode);
406                     break;
407             }
408         }
409     }
410     SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES);
411 }
412 
413 void
X11_QuitKeyboard(_THIS)414 X11_QuitKeyboard(_THIS)
415 {
416     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
417 
418 #if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM
419     if (data->xkb) {
420         X11_XkbFreeClientMap(data->xkb, 0, True);
421         data->xkb = NULL;
422     }
423 #endif
424 
425 #ifdef SDL_USE_IME
426     SDL_IME_Quit();
427 #endif
428 }
429 
430 static void
X11_ResetXIM(_THIS)431 X11_ResetXIM(_THIS)
432 {
433 #ifdef X_HAVE_UTF8_STRING
434     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
435     int i;
436 
437     if (videodata && videodata->windowlist) {
438         for (i = 0; i < videodata->numwindows; ++i) {
439             SDL_WindowData *data = videodata->windowlist[i];
440             if (data && data->ic) {
441                 /* Clear any partially entered dead keys */
442                 char *contents = X11_Xutf8ResetIC(data->ic);
443                 if (contents) {
444                     X11_XFree(contents);
445                 }
446             }
447         }
448     }
449 #endif
450 }
451 
452 void
X11_StartTextInput(_THIS)453 X11_StartTextInput(_THIS)
454 {
455     X11_ResetXIM(_this);
456 }
457 
458 void
X11_StopTextInput(_THIS)459 X11_StopTextInput(_THIS)
460 {
461     X11_ResetXIM(_this);
462 #ifdef SDL_USE_IME
463     SDL_IME_Reset();
464 #endif
465 }
466 
467 void
X11_SetTextInputRect(_THIS,SDL_Rect * rect)468 X11_SetTextInputRect(_THIS, SDL_Rect *rect)
469 {
470     if (!rect) {
471         SDL_InvalidParamError("rect");
472         return;
473     }
474 
475 #ifdef SDL_USE_IME
476     SDL_IME_UpdateTextRect(rect);
477 #endif
478 }
479 
480 #endif /* SDL_VIDEO_DRIVER_X11 */
481 
482 /* vi: set ts=4 sw=4 expandtab: */
483