Atlas - SDL_x11keyboard.c
Home / ext / SDL2 / src / video / x11 Lines: 5 | Size: 17173 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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* */ 41static 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 153static 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 */ 166static SDL_Scancode 167X11_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 199static Uint32 200X11_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 211KeySym 212X11_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 247int 248X11_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 const char *env_xmods = SDL_getenv("XMODIFIERS"); 296 SDL_bool has_dbus_ime_support = SDL_FALSE; 297 298 if (prev_locale) { 299 prev_locale = SDL_strdup(prev_locale); 300 } 301 302 if (prev_xmods) { 303 prev_xmods = SDL_strdup(prev_xmods); 304 } 305 306 /* IBus resends some key events that were filtered by XFilterEvents 307 when it is used via XIM which causes issues. Prevent this by forcing 308 @im=none if XMODIFIERS contains @im=ibus. IBus can still be used via 309 the DBus implementation, which also has support for pre-editing. */ 310 if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) { 311 has_dbus_ime_support = SDL_TRUE; 312 } 313 if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) { 314 has_dbus_ime_support = SDL_TRUE; 315 } 316 if (has_dbus_ime_support || !xkb_repeat) { 317 new_xmods = "@im=none"; 318 } 319 320 setlocale(LC_ALL, ""); 321 X11_XSetLocaleModifiers(new_xmods); 322 323 data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname); 324 325 /* Reset the locale + X locale modifiers back to how they were, 326 locale first because the X locale modifiers depend on it. */ 327 setlocale(LC_ALL, prev_locale); 328 X11_XSetLocaleModifiers(prev_xmods); 329 330 if (prev_locale) { 331 SDL_free(prev_locale); 332 } 333 334 if (prev_xmods) { 335 SDL_free(prev_xmods); 336 } 337 } 338#endif 339 /* Try to determine which scancodes are being used based on fingerprint */ 340 best_distance = SDL_arraysize(fingerprint) + 1; 341 best_index = -1; 342 X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode); 343 for (i = 0; i < SDL_arraysize(fingerprint); ++i) { 344 fingerprint[i].value = 345 X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) - 346 min_keycode; 347 } 348 for (i = 0; i < SDL_arraysize(scancode_set); ++i) { 349 /* Make sure the scancode set isn't too big */ 350 if ((max_keycode - min_keycode + 1) <= scancode_set[i].table_size) { 351 continue; 352 } 353 distance = 0; 354 for (j = 0; j < SDL_arraysize(fingerprint); ++j) { 355 if (fingerprint[j].value < 0 356 || fingerprint[j].value >= scancode_set[i].table_size) { 357 distance += 1; 358 } else if (scancode_set[i].table[fingerprint[j].value] != fingerprint[j].scancode) { 359 distance += 1; 360 } 361 } 362 if (distance < best_distance) { 363 best_distance = distance; 364 best_index = i; 365 } 366 } 367 if (best_index >= 0 && best_distance <= 2) { 368#ifdef DEBUG_KEYBOARD 369 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); 370#endif 371 SDL_memcpy(&data->key_layout[min_keycode], scancode_set[best_index].table, 372 sizeof(SDL_Scancode) * scancode_set[best_index].table_size); 373 } else { 374 SDL_Keycode keymap[SDL_NUM_SCANCODES]; 375 376 printf 377 ("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):\n"); 378 379 /* Determine key_layout - only works on US QWERTY layout */ 380 SDL_GetDefaultKeymap(keymap); 381 for (i = min_keycode; i <= max_keycode; ++i) { 382 KeySym sym; 383 sym = X11_KeyCodeToSym(_this, (KeyCode) i, 0); 384 if (sym != NoSymbol) { 385 SDL_Scancode scancode; 386 printf("code = %d, sym = 0x%X (%s) ", i - min_keycode, 387 (unsigned int) sym, X11_XKeysymToString(sym)); 388 scancode = X11_KeyCodeToSDLScancode(_this, i); 389 data->key_layout[i] = scancode; 390 if (scancode == SDL_SCANCODE_UNKNOWN) { 391 printf("scancode not found\n"); 392 } else { 393 printf("scancode = %d (%s)\n", scancode, SDL_GetScancodeName(scancode)); 394 } 395 } 396 } 397 } 398 399 X11_UpdateKeymap(_this); 400 401 SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); 402 403#ifdef SDL_USE_IME 404 SDL_IME_Init(); 405#endif 406 407 return 0; 408} 409 410void 411X11_UpdateKeymap(_THIS) 412{ 413 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 414 int i; 415 SDL_Scancode scancode; 416 SDL_Keycode keymap[SDL_NUM_SCANCODES]; 417 unsigned char group = 0; 418 419 SDL_GetDefaultKeymap(keymap); 420 421#if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM 422 if (data->xkb) { 423 XkbStateRec state; 424 X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb); 425 426 if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) { 427 group = state.group; 428 } 429 } 430#endif 431 432 433 for (i = 0; i < SDL_arraysize(data->key_layout); i++) { 434 Uint32 key; 435 436 /* Make sure this is a valid scancode */ 437 scancode = data->key_layout[i]; 438 if (scancode == SDL_SCANCODE_UNKNOWN) { 439 continue; 440 } 441 442 /* See if there is a UCS keycode for this scancode */ 443 key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group); 444 if (key) { 445 keymap[scancode] = key; 446 } else { 447 SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i); 448 449 switch (keyScancode) { 450 case SDL_SCANCODE_RETURN: 451 keymap[scancode] = SDLK_RETURN; 452 break; 453 case SDL_SCANCODE_ESCAPE: 454 keymap[scancode] = SDLK_ESCAPE; 455 break; 456 case SDL_SCANCODE_BACKSPACE: 457 keymap[scancode] = SDLK_BACKSPACE; 458 break; 459 case SDL_SCANCODE_TAB: 460 keymap[scancode] = SDLK_TAB; 461 break; 462 case SDL_SCANCODE_DELETE: 463 keymap[scancode] = SDLK_DELETE; 464 break; 465 default: 466 keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(keyScancode); 467 break; 468 } 469 } 470 } 471 SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES); 472} 473 474void 475X11_QuitKeyboard(_THIS) 476{ 477 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 478 479#if SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM 480 if (data->xkb) { 481 X11_XkbFreeKeyboard(data->xkb, 0, True); 482 data->xkb = NULL; 483 } 484#endif 485 486#ifdef SDL_USE_IME 487 SDL_IME_Quit(); 488#endif 489} 490 491static void 492X11_ResetXIM(_THIS) 493{ 494#ifdef X_HAVE_UTF8_STRING 495 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 496 int i; 497 498 if (videodata && videodata->windowlist) { 499 for (i = 0; i < videodata->numwindows; ++i) { 500 SDL_WindowData *data = videodata->windowlist[i]; 501 if (data && data->ic) { 502 /* Clear any partially entered dead keys */ 503 char *contents = X11_Xutf8ResetIC(data->ic); 504 if (contents) { 505 X11_XFree(contents); 506 } 507 } 508 } 509 } 510#endif 511} 512 513void 514X11_StartTextInput(_THIS) 515{ 516 X11_ResetXIM(_this); 517} 518 519void 520X11_StopTextInput(_THIS) 521{ 522 X11_ResetXIM(_this); 523#ifdef SDL_USE_IME 524 SDL_IME_Reset(); 525#endif 526} 527 528void 529X11_SetTextInputRect(_THIS, SDL_Rect *rect) 530{ 531 if (!rect) { 532 SDL_InvalidParamError("rect"); 533 return; 534 } 535 536#ifdef SDL_USE_IME 537 SDL_IME_UpdateTextRect(rect); 538#endif 539} 540 541#endif /* SDL_VIDEO_DRIVER_X11 */ 542 543/* vi: set ts=4 sw=4 expandtab: */ 544[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.