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