SDL_x11keyboard.c (17358B)
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: */