mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-05 19:08:12 +00:00
x11: Modernize and optimize key handling
- Use modern Xkb functions where appropriate and cleanly separate the modern and legacy paths. - Remove the deprecated XKeycodeToKeysym function in favor of directly querying the keymap on the legacy path. - Look up virtual modifiers by name on the Xkb path to better handle remapping (equivalent to the modifier handling under Wayland). - Optimize keymap creation on the Xkb path to cut keymap build times and enable fast group switching (equivalent to keymap handling on Wayland). - Enable and handle Xkb events to handle changes to the group, mapping, and modifier states. This is more reliable than using the legacy events (group changes may not arrive if the window lacks pointer focus), and better handles cases where modifiers are latched, locked, or activated externally rather than physically pressed.
This commit is contained in:

committed by
Sam Lantinga

parent
67e5130441
commit
f439e44771
@@ -390,7 +390,7 @@ macro(CheckX11)
|
||||
set(SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1)
|
||||
endif()
|
||||
|
||||
check_symbol_exists(XkbLookupKeySym "X11/Xlib.h;X11/XKBlib.h" SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM)
|
||||
check_include_file("X11/XKBlib.h" SDL_VIDEO_DRIVER_X11_HAS_XKBLIB)
|
||||
|
||||
if(SDL_X11_XCURSOR AND HAVE_XCURSOR_H AND XCURSOR_LIB)
|
||||
set(HAVE_X11_XCURSOR TRUE)
|
||||
|
@@ -422,7 +422,7 @@
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR@
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS @SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS@
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XTEST @SDL_VIDEO_DRIVER_X11_DYNAMIC_XTEST@
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM 1
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLIB 1
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_XCURSOR 1
|
||||
#cmakedefine SDL_VIDEO_DRIVER_X11_XDBE 1
|
||||
|
@@ -28,7 +28,7 @@
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xresource.h>
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
#include <X11/XKBlib.h>
|
||||
#endif
|
||||
|
||||
|
@@ -248,94 +248,202 @@ static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
|
||||
|
||||
static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata)
|
||||
{
|
||||
Window junk_window;
|
||||
int x, y;
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
if (viddata->keyboard.xkb_enabled) {
|
||||
XkbStateRec xkb_state;
|
||||
if (X11_XkbGetState(viddata->display, XkbUseCoreKbd, &xkb_state) == Success) {
|
||||
viddata->keyboard.pressed_modifiers = xkb_state.base_mods;
|
||||
viddata->keyboard.locked_modifiers = xkb_state.latched_mods | xkb_state.locked_mods;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
Window junk_window;
|
||||
int x, y;
|
||||
unsigned int mod_mask;
|
||||
|
||||
X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers);
|
||||
X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &mod_mask);
|
||||
viddata->keyboard.pressed_modifiers = mod_mask & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask);
|
||||
viddata->keyboard.locked_modifiers = mod_mask & (LockMask | viddata->keyboard.numlock_mask | viddata->keyboard.scrolllock_mask);
|
||||
}
|
||||
}
|
||||
|
||||
static void X11_ReconcileModifiers(SDL_VideoData *viddata)
|
||||
static void X11_ReconcileModifiers(SDL_VideoData *viddata, bool key_pressed)
|
||||
{
|
||||
const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers;
|
||||
|
||||
/* If a modifier was activated by a keypress, it will be tied to the
|
||||
* specific left/right key that initiated it. Otherwise, the ambiguous
|
||||
* left/right combo is used.
|
||||
/* Handle explicit pressed modifier state. This will correct the modifier state
|
||||
* if common modifier keys were remapped and the modifiers presumed to be set
|
||||
* during a key press event were incorrect, if the modifier was set to the
|
||||
* pressed state via means other than pressing the physical key, or if the
|
||||
* modifier state was set by a keypress before the corresponding key event
|
||||
* was received.
|
||||
*/
|
||||
if (xk_modifiers & ShiftMask) {
|
||||
if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT;
|
||||
if (key_pressed) {
|
||||
if (viddata->keyboard.pressed_modifiers & ShiftMask) {
|
||||
if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_SHIFT) {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_SHIFT;
|
||||
viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & ControlMask) {
|
||||
if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_CTRL) {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_CTRL;
|
||||
viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & viddata->keyboard.alt_mask) {
|
||||
if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_ALT) {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_ALT;
|
||||
viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_ALT);
|
||||
}
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & viddata->keyboard.gui_mask) {
|
||||
if (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_GUI) {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_GUI;
|
||||
viddata->keyboard.sdl_pressed_modifiers |= (viddata->keyboard.sdl_physically_pressed_modifiers & SDL_KMOD_GUI);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT;
|
||||
if (viddata->keyboard.pressed_modifiers & ShiftMask) {
|
||||
if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT)) {
|
||||
viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_SHIFT;
|
||||
}
|
||||
} else {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_SHIFT;
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & ControlMask) {
|
||||
if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL)) {
|
||||
viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_CTRL;
|
||||
}
|
||||
} else {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_CTRL;
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & viddata->keyboard.alt_mask) {
|
||||
if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT)) {
|
||||
viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_ALT;
|
||||
}
|
||||
} else {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_ALT;
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & viddata->keyboard.gui_mask) {
|
||||
if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI)) {
|
||||
viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_GUI;
|
||||
}
|
||||
} else {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_GUI;
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & viddata->keyboard.level3_mask) {
|
||||
if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_MODE)) {
|
||||
viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_MODE;
|
||||
}
|
||||
} else {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_MODE;
|
||||
}
|
||||
|
||||
if (viddata->keyboard.pressed_modifiers & viddata->keyboard.level5_mask) {
|
||||
if (!(viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_LEVEL5)) {
|
||||
viddata->keyboard.sdl_pressed_modifiers |= SDL_KMOD_LEVEL5;
|
||||
}
|
||||
} else {
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~SDL_KMOD_LEVEL5;
|
||||
}
|
||||
}
|
||||
|
||||
if (xk_modifiers & ControlMask) {
|
||||
if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL;
|
||||
/* If a latch or lock was activated by a keypress, the latch/lock will
|
||||
* be tied to the specific left/right key that initiated it. Otherwise,
|
||||
* the ambiguous left/right combo is used.
|
||||
*
|
||||
* The modifier will remain active until the latch/lock is released by
|
||||
* the system.
|
||||
*/
|
||||
if (viddata->keyboard.locked_modifiers & ShiftMask) {
|
||||
if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT) {
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SHIFT;
|
||||
viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_SHIFT);
|
||||
} else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_SHIFT)) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_SHIFT;
|
||||
}
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SHIFT;
|
||||
}
|
||||
|
||||
// Mod1 is used for the Alt keys
|
||||
if (xk_modifiers & Mod1Mask) {
|
||||
if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT;
|
||||
if (viddata->keyboard.locked_modifiers & ControlMask) {
|
||||
if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL) {
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CTRL;
|
||||
viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_CTRL);
|
||||
} else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_CTRL)) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_CTRL;
|
||||
}
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CTRL;
|
||||
}
|
||||
|
||||
// Mod4 is used for the Super (aka GUI/Logo) keys.
|
||||
if (xk_modifiers & Mod4Mask) {
|
||||
if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI;
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.alt_mask) {
|
||||
if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT) {
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_ALT;
|
||||
viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_ALT);
|
||||
} else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_ALT)) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_ALT;
|
||||
}
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_ALT;
|
||||
}
|
||||
|
||||
// Mod3 is typically Level 5 shift.
|
||||
if (xk_modifiers & Mod3Mask) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5;
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.gui_mask) {
|
||||
if (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI) {
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_GUI;
|
||||
viddata->keyboard.sdl_locked_modifiers |= (viddata->keyboard.sdl_pressed_modifiers & SDL_KMOD_GUI);
|
||||
} else if (!(viddata->keyboard.sdl_locked_modifiers & SDL_KMOD_GUI)) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_GUI;
|
||||
}
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_GUI;
|
||||
}
|
||||
|
||||
// Mod5 is typically Level 3 shift (aka AltGr).
|
||||
if (xk_modifiers & Mod5Mask) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE;
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.level3_mask) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_MODE;
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_MODE;
|
||||
}
|
||||
|
||||
if (xk_modifiers & LockMask) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS;
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.level5_mask) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_LEVEL5;
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_LEVEL5;
|
||||
}
|
||||
|
||||
if (xk_modifiers & viddata->xkb.numlock_mask) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM;
|
||||
// Capslock, Numlock, and Scrolllock can only be locked, not pressed.
|
||||
if (viddata->keyboard.locked_modifiers & LockMask) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_CAPS;
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_CAPS;
|
||||
}
|
||||
|
||||
if (xk_modifiers & viddata->xkb.scrolllock_mask) {
|
||||
viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL;
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.numlock_mask) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_NUM;
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL;
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_NUM;
|
||||
}
|
||||
|
||||
SDL_SetModState(viddata->xkb.sdl_modifiers);
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.scrolllock_mask) {
|
||||
viddata->keyboard.sdl_locked_modifiers |= SDL_KMOD_SCROLL;
|
||||
} else {
|
||||
viddata->keyboard.sdl_locked_modifiers &= ~SDL_KMOD_SCROLL;
|
||||
}
|
||||
|
||||
SDL_SetModState(viddata->keyboard.sdl_pressed_modifiers | viddata->keyboard.sdl_locked_modifiers);
|
||||
}
|
||||
|
||||
static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation)
|
||||
static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed)
|
||||
{
|
||||
const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
|
||||
SDL_Keymod mod = SDL_KMOD_NONE;
|
||||
bool reconcile = false;
|
||||
|
||||
/* SDL clients expect modifier state to be activated at the same time as the
|
||||
* source keypress, so we set pressed modifier state with the usual modifier
|
||||
@@ -378,48 +486,46 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode
|
||||
case SDLK_NUMLOCKCLEAR:
|
||||
case SDLK_SCROLLLOCK:
|
||||
{
|
||||
/* For locking modifier keys, query the lock state directly, or we may have to wait until the next
|
||||
* key press event to know if a lock was actually activated from the key event.
|
||||
*/
|
||||
unsigned int cur_mask = viddata->xkb.xkb_modifiers;
|
||||
X11_UpdateSystemKeyModifiers(viddata);
|
||||
// XKB provides the latched/locked state explicitly.
|
||||
if (viddata->keyboard.xkb_enabled) {
|
||||
/* For locking modifier keys, query the lock state directly, or we may have to wait until the next
|
||||
* key press event to know if a lock was actually activated from the key event.
|
||||
*/
|
||||
unsigned int cur_mask = viddata->keyboard.locked_modifiers;
|
||||
X11_UpdateSystemKeyModifiers(viddata);
|
||||
|
||||
if (viddata->xkb.xkb_modifiers & LockMask) {
|
||||
cur_mask |= LockMask;
|
||||
} else {
|
||||
cur_mask &= ~LockMask;
|
||||
}
|
||||
if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) {
|
||||
cur_mask |= viddata->xkb.numlock_mask;
|
||||
} else {
|
||||
cur_mask &= ~viddata->xkb.numlock_mask;
|
||||
}
|
||||
if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) {
|
||||
cur_mask |= viddata->xkb.scrolllock_mask;
|
||||
} else {
|
||||
cur_mask &= ~viddata->xkb.scrolllock_mask;
|
||||
}
|
||||
if (viddata->keyboard.locked_modifiers & LockMask) {
|
||||
cur_mask |= LockMask;
|
||||
} else {
|
||||
cur_mask &= ~LockMask;
|
||||
}
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.numlock_mask) {
|
||||
cur_mask |= viddata->keyboard.numlock_mask;
|
||||
} else {
|
||||
cur_mask &= ~viddata->keyboard.numlock_mask;
|
||||
}
|
||||
if (viddata->keyboard.locked_modifiers & viddata->keyboard.scrolllock_mask) {
|
||||
cur_mask |= viddata->keyboard.scrolllock_mask;
|
||||
} else {
|
||||
cur_mask &= ~viddata->keyboard.scrolllock_mask;
|
||||
}
|
||||
|
||||
viddata->xkb.xkb_modifiers = cur_mask;
|
||||
} SDL_FALLTHROUGH;
|
||||
viddata->keyboard.locked_modifiers = cur_mask;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
reconcile = true;
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pressed) {
|
||||
viddata->xkb.sdl_modifiers |= mod;
|
||||
viddata->keyboard.sdl_pressed_modifiers |= mod;
|
||||
viddata->keyboard.sdl_physically_pressed_modifiers |= mod;
|
||||
} else {
|
||||
viddata->xkb.sdl_modifiers &= ~mod;
|
||||
viddata->keyboard.sdl_pressed_modifiers &= ~mod;
|
||||
viddata->keyboard.sdl_physically_pressed_modifiers &= ~mod;
|
||||
}
|
||||
|
||||
if (allow_reconciliation) {
|
||||
if (reconcile) {
|
||||
X11_ReconcileModifiers(viddata);
|
||||
} else {
|
||||
SDL_SetModState(viddata->xkb.sdl_modifiers);
|
||||
}
|
||||
}
|
||||
X11_ReconcileModifiers(viddata, true);
|
||||
}
|
||||
|
||||
void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
|
||||
@@ -427,18 +533,25 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
Display *display = videodata->display;
|
||||
char keys[32];
|
||||
int keycode;
|
||||
const bool *keyboardState;
|
||||
|
||||
// Rebuild the modifier state in case it changed while focus was lost.
|
||||
X11_UpdateSystemKeyModifiers(videodata);
|
||||
X11_ReconcileModifiers(videodata, false);
|
||||
|
||||
// Keep caps, num, and scroll, but clear the others until we have updated key state.
|
||||
videodata->keyboard.sdl_pressed_modifiers = 0;
|
||||
videodata->keyboard.sdl_physically_pressed_modifiers = 0;
|
||||
videodata->keyboard.sdl_locked_modifiers &= SDL_KMOD_CAPS | SDL_KMOD_NUM | SDL_KMOD_SCROLL;
|
||||
videodata->keyboard.pressed_modifiers = 0;
|
||||
videodata->keyboard.locked_modifiers &= LockMask | videodata->keyboard.numlock_mask| videodata->keyboard.scrolllock_mask;
|
||||
|
||||
X11_XQueryKeymap(display, keys);
|
||||
|
||||
keyboardState = SDL_GetKeyboardState(0);
|
||||
for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) {
|
||||
SDL_Scancode scancode = videodata->key_layout[keycode];
|
||||
bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
|
||||
bool sdlKeyPressed = keyboardState[scancode];
|
||||
for (Uint32 keycode = 0; keycode < SDL_arraysize(videodata->keyboard.key_layout); ++keycode) {
|
||||
const SDL_Scancode scancode = videodata->keyboard.key_layout[keycode];
|
||||
const bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
|
||||
|
||||
if (x11KeyPressed && !sdlKeyPressed) {
|
||||
if (x11KeyPressed) {
|
||||
// Only update modifier state for keys that are pressed in another application
|
||||
switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) {
|
||||
case SDLK_LCTRL:
|
||||
@@ -451,20 +564,18 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
|
||||
case SDLK_RGUI:
|
||||
case SDLK_MODE:
|
||||
case SDLK_LEVEL5_SHIFT:
|
||||
X11_HandleModifierKeys(videodata, scancode, true, false);
|
||||
X11_HandleModifierKeys(videodata, scancode, true);
|
||||
SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (!x11KeyPressed && sdlKeyPressed) {
|
||||
X11_HandleModifierKeys(videodata, scancode, false, false);
|
||||
SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the latched/locked state for modifiers other than Caps, Num, and Scroll lock.
|
||||
X11_UpdateSystemKeyModifiers(videodata);
|
||||
X11_ReconcileModifiers(videodata);
|
||||
X11_ReconcileModifiers(videodata, false);
|
||||
}
|
||||
|
||||
static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data)
|
||||
@@ -933,7 +1044,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
|
||||
Status status = 0;
|
||||
bool handled_by_ime = false;
|
||||
bool pressed = (xevent->type == KeyPress);
|
||||
SDL_Scancode scancode = videodata->key_layout[keycode];
|
||||
SDL_Scancode scancode = videodata->keyboard.key_layout[keycode];
|
||||
Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time);
|
||||
|
||||
#ifdef DEBUG_XEVENTS
|
||||
@@ -951,7 +1062,12 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
|
||||
#endif // DEBUG SCANCODES
|
||||
|
||||
text[0] = '\0';
|
||||
videodata->xkb.xkb_modifiers = xevent->xkey.state;
|
||||
|
||||
// XKB updates the modifiers explicitly via a state event.
|
||||
if (!videodata->keyboard.xkb_enabled) {
|
||||
videodata->keyboard.pressed_modifiers = xevent->xkey.state & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask);
|
||||
videodata->keyboard.locked_modifiers = xevent->xkey.state & (LockMask | videodata->keyboard.numlock_mask | videodata->keyboard.scrolllock_mask);
|
||||
}
|
||||
|
||||
if (SDL_TextInputActive(windowdata->window)) {
|
||||
// filter events catches XIM events and sends them to the correct handler
|
||||
@@ -979,7 +1095,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
|
||||
|
||||
if (!handled_by_ime) {
|
||||
if (pressed) {
|
||||
X11_HandleModifierKeys(videodata, scancode, true, true);
|
||||
X11_HandleModifierKeys(videodata, scancode, true);
|
||||
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true);
|
||||
|
||||
if (*text && !(SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) {
|
||||
@@ -993,7 +1109,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
|
||||
return;
|
||||
}
|
||||
|
||||
X11_HandleModifierKeys(videodata, scancode, false, true);
|
||||
X11_HandleModifierKeys(videodata, scancode, false);
|
||||
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false);
|
||||
}
|
||||
}
|
||||
@@ -1225,37 +1341,78 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
||||
|
||||
if (!data) {
|
||||
// The window for KeymapNotify, etc events is 0
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
if (videodata->keyboard.xkb_enabled && xevent->type == videodata->keyboard.xkb.event) {
|
||||
XkbEvent *xkbevent = (XkbEvent *)xevent;
|
||||
switch (xkbevent->any.xkb_type) {
|
||||
case XkbStateNotify:
|
||||
{
|
||||
#ifdef DEBUG_XEVENTS
|
||||
SDL_Log("window 0x%lx: XkbStateNotify!", xevent->xany.window);
|
||||
#endif
|
||||
if ((xkbevent->state.changed & XkbGroupStateMask) && xkbevent->state.group != videodata->keyboard.xkb.current_group) {
|
||||
videodata->keyboard.xkb.current_group = xkbevent->state.group;
|
||||
SDL_SetKeymap(videodata->keyboard.xkb.keymaps[videodata->keyboard.xkb.current_group], true);
|
||||
}
|
||||
|
||||
if (xkbevent->state.changed & XkbModifierStateMask) {
|
||||
videodata->keyboard.pressed_modifiers = xkbevent->state.base_mods;
|
||||
videodata->keyboard.locked_modifiers = xkbevent->state.latched_mods | xkbevent->state.locked_mods;
|
||||
X11_ReconcileModifiers(videodata, false);
|
||||
}
|
||||
} break;
|
||||
|
||||
case XkbMapNotify:
|
||||
#ifdef DEBUG_XEVENTS
|
||||
SDL_Log("window 0x%lx: XkbMapNotify!", xevent->xany.window);
|
||||
SDL_FALLTHROUGH;
|
||||
#endif
|
||||
case XkbNewKeyboardNotify:
|
||||
{
|
||||
#ifdef DEBUG_XEVENTS
|
||||
if (xkbevent->any.xkb_type == XkbNewKeyboardNotify) {
|
||||
SDL_Log("window 0x%lx: XkbNewKeyboardNotify!", xevent->xany.window);
|
||||
}
|
||||
#endif
|
||||
X11_XkbRefreshKeyboardMapping(&xkbevent->map);
|
||||
|
||||
// Don't redundantly rebuild the keymap if this is a duplicate event.
|
||||
if (xkbevent->any.serial != videodata->keyboard.xkb.last_map_serial) {
|
||||
videodata->keyboard.xkb.last_map_serial = xkbevent->any.serial;
|
||||
X11_UpdateKeymap(_this, true);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (xevent->type == KeymapNotify) {
|
||||
#ifdef DEBUG_XEVENTS
|
||||
SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window);
|
||||
#endif
|
||||
if (SDL_GetKeyboardFocus() != NULL) {
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
if (videodata->xkb.desc_ptr) {
|
||||
XkbStateRec state;
|
||||
if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) {
|
||||
if (state.group != videodata->xkb.current_group) {
|
||||
// Only rebuild the keymap if the layout has changed.
|
||||
videodata->xkb.current_group = state.group;
|
||||
X11_UpdateKeymap(_this, true);
|
||||
}
|
||||
}
|
||||
if (!videodata->keyboard.xkb_enabled) {
|
||||
if (SDL_GetKeyboardFocus() != NULL) {
|
||||
X11_UpdateKeymap(_this, true);
|
||||
}
|
||||
#endif
|
||||
X11_ReconcileKeyboardState(_this);
|
||||
}
|
||||
|
||||
X11_ReconcileKeyboardState(_this);
|
||||
} else if (xevent->type == MappingNotify) {
|
||||
// Has the keyboard layout changed?
|
||||
const int request = xevent->xmapping.request;
|
||||
if (!videodata->keyboard.xkb_enabled) {
|
||||
// Has the keyboard layout changed?
|
||||
const int request = xevent->xmapping.request;
|
||||
|
||||
#ifdef DEBUG_XEVENTS
|
||||
SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window);
|
||||
SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window);
|
||||
#endif
|
||||
if ((request == MappingKeyboard) || (request == MappingModifier)) {
|
||||
X11_XRefreshKeyboardMapping(&xevent->xmapping);
|
||||
}
|
||||
if ((request == MappingKeyboard) || (request == MappingModifier)) {
|
||||
X11_XRefreshKeyboardMapping(&xevent->xmapping);
|
||||
}
|
||||
|
||||
X11_UpdateKeymap(_this, true);
|
||||
X11_UpdateKeymap(_this, true);
|
||||
}
|
||||
} else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) {
|
||||
char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
|
||||
|
||||
|
@@ -28,7 +28,10 @@
|
||||
#include "../../events/SDL_scancode_tables_c.h"
|
||||
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
#include <X11/XKBlib.h>
|
||||
#endif
|
||||
|
||||
#include "../../events/imKStoUCS.h"
|
||||
#include "../../events/SDL_keysym_to_scancode_c.h"
|
||||
@@ -70,6 +73,28 @@ static bool X11_ScancodeIsRemappable(SDL_Scancode scancode)
|
||||
}
|
||||
}
|
||||
|
||||
static KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned int group, unsigned int level)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
KeySym keysym;
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
if (data->keyboard.xkb_enabled) {
|
||||
keysym = X11_XkbKeycodeToKeysym(data->display, keycode, group, level);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// TODO: Handle groups on the legacy path.
|
||||
if (keycode >= data->keyboard.core.min_keycode && keycode <= data->keyboard.core.max_keycode) {
|
||||
keysym = data->keyboard.core.keysym_map[(keycode - data->keyboard.core.min_keycode) * data->keyboard.core.keysyms_per_key];
|
||||
} else {
|
||||
keysym = NoSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
return keysym;
|
||||
}
|
||||
|
||||
// This function only correctly maps letters and numbers for keyboards in US QWERTY layout
|
||||
static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode keycode)
|
||||
{
|
||||
@@ -82,48 +107,6 @@ static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode key
|
||||
return SDL_GetScancodeFromKeySym(keysym, keycode);
|
||||
}
|
||||
|
||||
KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group, unsigned int mod_mask)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
KeySym keysym;
|
||||
unsigned int mods_ret[16];
|
||||
|
||||
SDL_zero(mods_ret);
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
if (data->xkb.desc_ptr) {
|
||||
int num_groups = XkbKeyNumGroups(data->xkb.desc_ptr, keycode);
|
||||
unsigned char info = XkbKeyGroupInfo(data->xkb.desc_ptr, keycode);
|
||||
|
||||
if (num_groups && group >= num_groups) {
|
||||
|
||||
int action = XkbOutOfRangeGroupAction(info);
|
||||
|
||||
if (action == XkbRedirectIntoRange) {
|
||||
group = XkbOutOfRangeGroupNumber(info);
|
||||
if (group >= num_groups) {
|
||||
group = 0;
|
||||
}
|
||||
} else if (action == XkbClampIntoRange) {
|
||||
group = num_groups - 1;
|
||||
} else {
|
||||
group %= num_groups;
|
||||
}
|
||||
}
|
||||
|
||||
if (X11_XkbLookupKeySym(data->display, keycode, XkbBuildCoreState(mod_mask, group), mods_ret, &keysym) == NoSymbol) {
|
||||
keysym = NoSymbol;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// TODO: Handle groups and modifiers on the legacy path.
|
||||
keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
|
||||
}
|
||||
|
||||
return keysym;
|
||||
}
|
||||
|
||||
bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
@@ -146,21 +129,33 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
int best_distance;
|
||||
int best_index;
|
||||
int distance;
|
||||
Bool xkb_repeat = 0;
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
{
|
||||
int xkb_major = XkbMajorVersion;
|
||||
int xkb_minor = XkbMinorVersion;
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
int xkb_major = XkbMajorVersion;
|
||||
int xkb_minor = XkbMinorVersion;
|
||||
|
||||
if (X11_XkbQueryExtension(data->display, NULL, &data->xkb.event, NULL, &xkb_major, &xkb_minor)) {
|
||||
data->xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
|
||||
}
|
||||
if (X11_XkbQueryExtension(data->display, NULL, &data->keyboard.xkb.event, NULL, &xkb_major, &xkb_minor)) {
|
||||
Bool xkb_repeat = 0;
|
||||
data->keyboard.xkb_enabled = true;
|
||||
data->keyboard.xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
|
||||
|
||||
// This will remove KeyRelease events for held keys
|
||||
// This will remove KeyRelease events for held keys.
|
||||
X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
|
||||
}
|
||||
|
||||
// Enable the key mapping and state events.
|
||||
X11_XkbSelectEvents(data->display, XkbUseCoreKbd,
|
||||
XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask,
|
||||
XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask);
|
||||
X11_XkbSelectEventDetails(data->display, XkbUseCoreKbd, XkbStateNotify, XkbGroupStateMask | XkbModifierStateMask, XkbGroupStateMask | XkbModifierStateMask);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// If XKB isn't available, initialize the legacy path.
|
||||
X11_XDisplayKeycodes(data->display, &data->keyboard.core.min_keycode, &data->keyboard.core.max_keycode);
|
||||
data->keyboard.core.keysym_map = X11_XGetKeyboardMapping(data->display, data->keyboard.core.min_keycode,
|
||||
data->keyboard.core.max_keycode - data->keyboard.core.min_keycode,
|
||||
&data->keyboard.core.keysyms_per_key);
|
||||
}
|
||||
|
||||
// Open a connection to the X input manager
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
@@ -242,10 +237,10 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
SDL_Log("Using scancode set %d, min_keycode = %d, max_keycode = %d, table_size = %d", best_index, min_keycode, max_keycode, table_size);
|
||||
#endif
|
||||
// This should never happen, but just in case...
|
||||
if (table_size > (SDL_arraysize(data->key_layout) - min_keycode)) {
|
||||
table_size = (SDL_arraysize(data->key_layout) - min_keycode);
|
||||
if (table_size > (SDL_arraysize(data->keyboard.key_layout) - min_keycode)) {
|
||||
table_size = (SDL_arraysize(data->keyboard.key_layout) - min_keycode);
|
||||
}
|
||||
SDL_memcpy(&data->key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size);
|
||||
SDL_memcpy(&data->keyboard.key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size);
|
||||
|
||||
/* Scancodes represent physical locations on the keyboard, unaffected by keyboard mapping.
|
||||
However, there are a number of extended scancodes that have no standard location, so use
|
||||
@@ -261,7 +256,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
(unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym));
|
||||
}
|
||||
#endif
|
||||
if (scancode == data->key_layout[i]) {
|
||||
if (scancode == data->keyboard.key_layout[i]) {
|
||||
continue;
|
||||
}
|
||||
if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & (SDLK_SCANCODE_MASK | SDLK_EXTENDED_MASK)) && X11_ScancodeIsRemappable(scancode)) {
|
||||
@@ -269,7 +264,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
#ifdef DEBUG_KEYBOARD
|
||||
SDL_Log("Changing scancode, was %d (%s), now %d (%s)", data->key_layout[i], SDL_GetScancodeName(data->key_layout[i]), scancode, SDL_GetScancodeName(scancode));
|
||||
#endif
|
||||
data->key_layout[i] = scancode;
|
||||
data->keyboard.key_layout[i] = scancode;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -293,7 +288,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
SDL_Log("scancode = %d (%s)", scancode, SDL_GetScancodeName(scancode));
|
||||
}
|
||||
#endif
|
||||
data->key_layout[i] = scancode;
|
||||
data->keyboard.key_layout[i] = scancode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,149 +301,235 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned X11_GetNumLockModifierMask(SDL_VideoDevice *_this)
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
static unsigned int X11_GetXkbVirtualModifierMask(SDL_VideoDevice *_this, const char *vmod_name)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
unsigned int mod_mask = 0;
|
||||
|
||||
if (videodata->keyboard.xkb_enabled) {
|
||||
Atom vmod = X11_XInternAtom(videodata->display, vmod_name, True);
|
||||
if (vmod != None) {
|
||||
for (int i = 0; i < XkbNumVirtualMods; ++i) {
|
||||
if (vmod == videodata->keyboard.xkb.desc_ptr->names->vmods[i]) {
|
||||
mod_mask = videodata->keyboard.xkb.desc_ptr->server->vmods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod_mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned X11_GetXModifierMask(SDL_VideoDevice *_this, SDL_Scancode scancode)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
Display *display = videodata->display;
|
||||
unsigned num_mask = 0;
|
||||
int i, j;
|
||||
XModifierKeymap *xmods;
|
||||
unsigned n;
|
||||
unsigned int mod_mask = 0;
|
||||
|
||||
xmods = X11_XGetModifierMapping(display);
|
||||
n = xmods->max_keypermod;
|
||||
for (i = 3; i < 8; i++) {
|
||||
for (j = 0; j < n; j++) {
|
||||
KeyCode kc = xmods->modifiermap[i * n + j];
|
||||
if (videodata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) {
|
||||
num_mask = 1 << i;
|
||||
XModifierKeymap *xmods = X11_XGetModifierMapping(display);
|
||||
unsigned int n = xmods->max_keypermod;
|
||||
for (int i = 3; i < 8; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
const KeyCode kc = xmods->modifiermap[i * n + j];
|
||||
if (videodata->keyboard.key_layout[kc] == scancode) {
|
||||
mod_mask = 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
X11_XFreeModifiermap(xmods);
|
||||
|
||||
return num_mask;
|
||||
return mod_mask;
|
||||
}
|
||||
|
||||
static unsigned X11_GetScrollLockModifierMask(SDL_VideoDevice *_this)
|
||||
static void X11_AddKeymapEntry(SDL_Keymap *keymap, Uint32 xkeycode, KeySym xkeysym, SDL_Scancode sdl_scancode, SDL_Keymod sdl_mod_mask)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
Display *display = videodata->display;
|
||||
unsigned num_mask = 0;
|
||||
int i, j;
|
||||
XModifierKeymap *xmods;
|
||||
unsigned n;
|
||||
SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(xkeysym, xkeycode, sdl_mod_mask);
|
||||
|
||||
xmods = X11_XGetModifierMapping(display);
|
||||
n = xmods->max_keypermod;
|
||||
for (i = 3; i < 8; i++) {
|
||||
for (j = 0; j < n; j++) {
|
||||
KeyCode kc = xmods->modifiermap[i * n + j];
|
||||
if (videodata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) {
|
||||
num_mask = 1 << i;
|
||||
break;
|
||||
}
|
||||
if (!keycode) {
|
||||
switch (sdl_scancode) {
|
||||
case SDL_SCANCODE_RETURN:
|
||||
keycode = SDLK_RETURN;
|
||||
break;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
keycode = SDLK_ESCAPE;
|
||||
break;
|
||||
case SDL_SCANCODE_BACKSPACE:
|
||||
keycode = SDLK_BACKSPACE;
|
||||
break;
|
||||
case SDL_SCANCODE_DELETE:
|
||||
keycode = SDLK_DELETE;
|
||||
break;
|
||||
default:
|
||||
keycode = SDL_SCANCODE_TO_KEYCODE(sdl_scancode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
X11_XFreeModifiermap(xmods);
|
||||
|
||||
return num_mask;
|
||||
SDL_SetKeymapEntry(keymap, sdl_scancode, sdl_mod_mask, keycode);
|
||||
}
|
||||
|
||||
void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event)
|
||||
{
|
||||
struct Keymod_masks
|
||||
{
|
||||
SDL_Keymod sdl_mask;
|
||||
unsigned int xkb_mask;
|
||||
} const keymod_masks[] = {
|
||||
{ SDL_KMOD_NONE, 0 },
|
||||
{ SDL_KMOD_SHIFT, ShiftMask },
|
||||
{ SDL_KMOD_CAPS, LockMask },
|
||||
{ SDL_KMOD_SHIFT | SDL_KMOD_CAPS, ShiftMask | LockMask },
|
||||
{ SDL_KMOD_MODE, Mod5Mask },
|
||||
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod5Mask | ShiftMask },
|
||||
{ SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod5Mask | LockMask },
|
||||
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod5Mask | ShiftMask | LockMask },
|
||||
{ SDL_KMOD_LEVEL5, Mod3Mask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, Mod3Mask | ShiftMask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, Mod3Mask | LockMask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | ShiftMask | LockMask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, Mod5Mask | Mod3Mask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod3Mask | Mod5Mask | ShiftMask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | LockMask },
|
||||
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | ShiftMask | LockMask }
|
||||
};
|
||||
|
||||
SDL_VideoData *data = _this->internal;
|
||||
SDL_Scancode scancode;
|
||||
SDL_Keymap *keymap = SDL_CreateKeymap(true);
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
if (data->xkb.desc_ptr) {
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
if (data->keyboard.xkb_enabled) {
|
||||
XkbStateRec state;
|
||||
X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb.desc_ptr);
|
||||
|
||||
SDL_SetKeymap(NULL, false);
|
||||
for (unsigned int i = 0; i < XkbNumKbdGroups; ++i) {
|
||||
SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]);
|
||||
data->keyboard.xkb.keymaps[i] = SDL_CreateKeymap(false);
|
||||
}
|
||||
|
||||
X11_XkbGetNames(data->display, XkbVirtualModNamesMask, data->keyboard.xkb.desc_ptr);
|
||||
X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask | XkbVirtualModsMask, data->keyboard.xkb.desc_ptr);
|
||||
|
||||
if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
|
||||
data->xkb.current_group = state.group;
|
||||
data->keyboard.xkb.current_group = state.group;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) {
|
||||
for (int i = 0; i < SDL_arraysize(data->key_layout); ++i) {
|
||||
// Make sure this is a valid scancode
|
||||
scancode = data->key_layout[i];
|
||||
data->keyboard.alt_mask = X11_GetXkbVirtualModifierMask(_this, "Alt");
|
||||
if (!data->keyboard.alt_mask) {
|
||||
data->keyboard.alt_mask = X11_GetXkbVirtualModifierMask(_this, "Meta");
|
||||
}
|
||||
data->keyboard.gui_mask = X11_GetXkbVirtualModifierMask(_this, "Super");
|
||||
data->keyboard.level3_mask = X11_GetXkbVirtualModifierMask(_this, "LevelThree");
|
||||
data->keyboard.level5_mask = X11_GetXkbVirtualModifierMask(_this, "LevelFive");
|
||||
data->keyboard.numlock_mask = X11_GetXkbVirtualModifierMask(_this, "NumLock");
|
||||
data->keyboard.scrolllock_mask = X11_GetXkbVirtualModifierMask(_this, "ScrollLock");
|
||||
|
||||
const Uint32 valid_mod_mask = ShiftMask | LockMask | data->keyboard.alt_mask | data->keyboard.level3_mask | data->keyboard.level5_mask;
|
||||
|
||||
for (Uint32 xkeycode = data->keyboard.xkb.desc_ptr->min_key_code; xkeycode < data->keyboard.xkb.desc_ptr->max_key_code; ++xkeycode) {
|
||||
const SDL_Scancode scancode = data->keyboard.key_layout[xkeycode];
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb.current_group, keymod_masks[m].xkb_mask);
|
||||
for (Uint32 group = 0; group < XkbNumKbdGroups; ++group) {
|
||||
SDL_Keymap *keymap = data->keyboard.xkb.keymaps[group];
|
||||
|
||||
if (keysym != NoSymbol) {
|
||||
SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(keysym, i, keymod_masks[m].sdl_mask);
|
||||
Uint32 effective_group = group;
|
||||
const unsigned char max_key_group = XkbKeyNumGroups(data->keyboard.xkb.desc_ptr, xkeycode);
|
||||
const unsigned char key_group_info = XkbKeyGroupInfo(data->keyboard.xkb.desc_ptr, xkeycode);
|
||||
|
||||
if (!keycode) {
|
||||
switch (scancode) {
|
||||
case SDL_SCANCODE_RETURN:
|
||||
keycode = SDLK_RETURN;
|
||||
break;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
keycode = SDLK_ESCAPE;
|
||||
break;
|
||||
case SDL_SCANCODE_BACKSPACE:
|
||||
keycode = SDLK_BACKSPACE;
|
||||
break;
|
||||
case SDL_SCANCODE_DELETE:
|
||||
keycode = SDLK_DELETE;
|
||||
break;
|
||||
if (max_key_group && effective_group >= max_key_group) {
|
||||
const unsigned char action = XkbOutOfRangeGroupAction(key_group_info);
|
||||
|
||||
switch (action) {
|
||||
default:
|
||||
keycode = SDL_SCANCODE_TO_KEYCODE(scancode);
|
||||
effective_group %= max_key_group;
|
||||
break;
|
||||
case XkbClampIntoRange:
|
||||
effective_group = max_key_group - 1;
|
||||
break;
|
||||
case XkbRedirectIntoRange:
|
||||
effective_group = XkbOutOfRangeGroupNumber(key_group_info);
|
||||
if (effective_group >= max_key_group) {
|
||||
effective_group = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode);
|
||||
XkbKeyTypePtr key_type = XkbKeyKeyType(data->keyboard.xkb.desc_ptr, xkeycode, effective_group);
|
||||
|
||||
for (Uint32 level = 0; level < key_type->num_levels; ++level) {
|
||||
const KeySym keysym = X11_KeyCodeToSym(_this, xkeycode, effective_group, level);
|
||||
|
||||
if (keysym != NoSymbol) {
|
||||
bool key_added = false;
|
||||
|
||||
for (int map_idx = 0; map_idx < key_type->map_count; ++map_idx) {
|
||||
if (key_type->map[map_idx].active && key_type->map[map_idx].level == level) {
|
||||
const unsigned int xkb_mod_mask = key_type->map[map_idx].mods.mask;
|
||||
if ((xkb_mod_mask | valid_mod_mask) == valid_mod_mask) {
|
||||
const SDL_Keymod sdl_mod_mask = (xkb_mod_mask & ShiftMask ? SDL_KMOD_SHIFT : 0) |
|
||||
(xkb_mod_mask & LockMask ? SDL_KMOD_CAPS : 0) |
|
||||
(xkb_mod_mask & data->keyboard.alt_mask ? SDL_KMOD_ALT : 0) |
|
||||
(xkb_mod_mask & data->keyboard.level3_mask ? SDL_KMOD_MODE : 0) |
|
||||
(xkb_mod_mask & data->keyboard.level5_mask ? SDL_KMOD_LEVEL5 : 0);
|
||||
|
||||
X11_AddKeymapEntry(keymap, xkeycode, keysym, scancode, sdl_mod_mask);
|
||||
key_added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the unmodified key for level 0.
|
||||
if (!level && !key_added) {
|
||||
X11_AddKeymapEntry(keymap, xkeycode, keysym, scancode, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data->xkb.numlock_mask = X11_GetNumLockModifierMask(_this);
|
||||
data->xkb.scrolllock_mask = X11_GetScrollLockModifierMask(_this);
|
||||
SDL_SetKeymap(keymap, send_event);
|
||||
SDL_SetKeymap(data->keyboard.xkb.keymaps[data->keyboard.xkb.current_group], send_event);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
SDL_Keymap *keymap = SDL_CreateKeymap(true);
|
||||
|
||||
if (send_event) {
|
||||
if (data->keyboard.core.keysym_map) {
|
||||
X11_XFree(data->keyboard.core.keysym_map);
|
||||
}
|
||||
X11_XDisplayKeycodes(data->display, &data->keyboard.core.min_keycode, &data->keyboard.core.max_keycode);
|
||||
data->keyboard.core.keysym_map = X11_XGetKeyboardMapping(data->display, data->keyboard.core.min_keycode,
|
||||
data->keyboard.core.max_keycode - data->keyboard.core.min_keycode,
|
||||
&data->keyboard.core.keysyms_per_key);
|
||||
}
|
||||
|
||||
for (Uint32 xkeycode = data->keyboard.core.min_keycode; xkeycode <= data->keyboard.core.max_keycode; ++xkeycode) {
|
||||
const SDL_Scancode scancode = data->keyboard.key_layout[xkeycode];
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const KeySym keysym = X11_KeyCodeToSym(_this, xkeycode, 0, 0);
|
||||
if (keysym != NoSymbol) {
|
||||
X11_AddKeymapEntry(keymap, xkeycode, keysym, scancode, 0);
|
||||
}
|
||||
}
|
||||
|
||||
data->keyboard.alt_mask = Mod1Mask; // Alt or Meta
|
||||
data->keyboard.gui_mask = Mod4Mask; // Super
|
||||
data->keyboard.level3_mask = Mod5Mask; // Note: Not a typo, Mod5 = level 3 shift, and Mod3 = level 5 shift.
|
||||
data->keyboard.level5_mask = Mod3Mask;
|
||||
data->keyboard.numlock_mask = X11_GetXModifierMask(_this, SDL_SCANCODE_NUMLOCKCLEAR);
|
||||
data->keyboard.scrolllock_mask = X11_GetXModifierMask(_this, SDL_SCANCODE_SCROLLLOCK);
|
||||
|
||||
SDL_SetKeymap(keymap, send_event);
|
||||
}
|
||||
}
|
||||
|
||||
void X11_QuitKeyboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
if (data->xkb.desc_ptr) {
|
||||
X11_XkbFreeKeyboard(data->xkb.desc_ptr, 0, True);
|
||||
data->xkb.desc_ptr = NULL;
|
||||
}
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
if (data->keyboard.xkb_enabled) {
|
||||
for (int i = 0; i < XkbNumKbdGroups; ++i) {
|
||||
SDL_DestroyKeymap(data->keyboard.xkb.keymaps[i]);
|
||||
data->keyboard.xkb.keymaps[i] = NULL;
|
||||
}
|
||||
|
||||
if (data->keyboard.xkb_enabled) {
|
||||
X11_XkbFreeKeyboard(data->keyboard.xkb.desc_ptr, 0, True);
|
||||
data->keyboard.xkb.desc_ptr = NULL;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (data->keyboard.core.keysym_map) {
|
||||
X11_XFree(data->keyboard.core.keysym_map);
|
||||
data->keyboard.core.keysym_map = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void X11_ClearComposition(SDL_WindowData *data)
|
||||
|
@@ -35,6 +35,5 @@ extern bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
|
||||
extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
|
||||
extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask);
|
||||
|
||||
#endif // SDL_x11keyboard_h_
|
||||
|
@@ -71,6 +71,7 @@ SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b))
|
||||
SDL_X11_SYM(void,XFreeStringList,(char** a))
|
||||
SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b))
|
||||
SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c))
|
||||
SDL_X11_SYM(KeySym*,XGetKeyboardMapping,(Display *a, KeyCode b, int c, int *d))
|
||||
SDL_X11_SYM(int,XGetErrorDatabaseText,(Display* a,_Xconst char* b,_Xconst char* c,_Xconst char* d,char* e,int f))
|
||||
SDL_X11_SYM(XModifierKeymap*,XGetModifierMapping,(Display* a))
|
||||
SDL_X11_SYM(int,XGetPointerControl,(Display* a,int* b,int* c,int* d))
|
||||
@@ -196,35 +197,21 @@ SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b))
|
||||
SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b))
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
SDL_X11_SYM(Bool,XkbQueryExtension,(Display* a,int * b,int * c,int * d,int * e, int *f))
|
||||
#if NeedWidePrototypes
|
||||
SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, unsigned int b, unsigned int c, unsigned int* d, KeySym* e))
|
||||
#else
|
||||
SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, KeyCode b, unsigned int c, unsigned int* d, KeySym* e))
|
||||
#endif
|
||||
SDL_X11_SYM(KeySym,XkbKeycodeToKeysym,(Display* a, KeyCode b, unsigned int c, unsigned int d))
|
||||
SDL_X11_SYM(Bool,XkbSelectEvents,(Display* a, unsigned int b, unsigned int c, unsigned long d))
|
||||
SDL_X11_SYM(Bool,XkbSelectEventDetails,(Display* a, unsigned int b, unsigned int c, unsigned long d, unsigned long e))
|
||||
SDL_X11_SYM(Status,XkbGetNames,(Display *a, unsigned int b, XkbDescPtr c))
|
||||
SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c))
|
||||
SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c))
|
||||
SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c))
|
||||
SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c))
|
||||
SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c))
|
||||
SDL_X11_SYM(Status,XkbRefreshKeyboardMapping,(XkbMapNotifyEvent *a))
|
||||
SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c))
|
||||
#endif
|
||||
|
||||
// XKeycodeToKeysym is a deprecated function
|
||||
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
#if NeedWidePrototypes
|
||||
SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,unsigned int b,int c))
|
||||
#else
|
||||
SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,KeyCode b,int c))
|
||||
#endif
|
||||
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
SDL_X11_MODULE(UTF8)
|
||||
SDL_X11_SYM(int,Xutf8TextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e))
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#define SDL_x11video_h_
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../../events/SDL_keymap_c.h"
|
||||
|
||||
#include "../../core/linux/SDL_dbus.h"
|
||||
#include "../../core/linux/SDL_ime.h"
|
||||
@@ -125,7 +126,6 @@ struct SDL_VideoData
|
||||
Atom pen_atom_wacom_tool_type;
|
||||
} atoms;
|
||||
|
||||
SDL_Scancode key_layout[256];
|
||||
bool selection_waiting;
|
||||
bool selection_incr_waiting;
|
||||
|
||||
@@ -144,21 +144,43 @@ struct SDL_VideoData
|
||||
int xrandr_event_base;
|
||||
struct
|
||||
{
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
|
||||
XkbDescPtr desc_ptr;
|
||||
bool xkb_enabled;
|
||||
SDL_Scancode key_layout[256];
|
||||
|
||||
union
|
||||
{
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLIB
|
||||
struct
|
||||
{
|
||||
XkbDescPtr desc_ptr;
|
||||
SDL_Keymap *keymaps[XkbNumKbdGroups];
|
||||
unsigned long last_map_serial;
|
||||
int event;
|
||||
Uint32 current_group;
|
||||
} xkb; // Modern XKB keyboard handling
|
||||
#endif
|
||||
int event;
|
||||
unsigned int current_group;
|
||||
unsigned int xkb_modifiers;
|
||||
|
||||
SDL_Keymod sdl_modifiers;
|
||||
struct
|
||||
{
|
||||
KeySym *keysym_map;
|
||||
int keysyms_per_key;
|
||||
int min_keycode;
|
||||
int max_keycode;
|
||||
} core; // Legacy core keyboard handling
|
||||
};
|
||||
Uint32 pressed_modifiers;
|
||||
Uint32 locked_modifiers;
|
||||
SDL_Keymod sdl_pressed_modifiers;
|
||||
SDL_Keymod sdl_physically_pressed_modifiers;
|
||||
SDL_Keymod sdl_locked_modifiers;
|
||||
|
||||
// Virtual modifiers looked up by name.
|
||||
Uint32 alt_mask;
|
||||
Uint32 gui_mask;
|
||||
Uint32 level3_mask;
|
||||
Uint32 level5_mask;
|
||||
Uint32 numlock_mask;
|
||||
Uint32 scrolllock_mask;
|
||||
} xkb;
|
||||
|
||||
KeyCode filter_code;
|
||||
Time filter_time;
|
||||
} keyboard;
|
||||
|
||||
#ifdef SDL_VIDEO_VULKAN
|
||||
// Vulkan variables only valid if _this->vulkan_config.loader_handle is not NULL
|
||||
|
Reference in New Issue
Block a user