x11: use XInput2 for lower level access to keyboard events
This commit is contained in:
parent
012fc1e32b
commit
658f3cdcf1
@ -832,6 +832,99 @@ SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent)
|
||||
{
|
||||
SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
|
||||
Display *display = videodata->display;
|
||||
KeyCode keycode = xevent->xkey.keycode;
|
||||
KeySym keysym = NoSymbol;
|
||||
char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
||||
Status status = 0;
|
||||
SDL_bool handled_by_ime = SDL_FALSE;
|
||||
|
||||
/* Save the original keycode for dead keys, which are filtered out by
|
||||
the XFilterEvent() call below.
|
||||
*/
|
||||
int orig_event_type = xevent->type;
|
||||
KeyCode orig_keycode = xevent->xkey.keycode;
|
||||
|
||||
/* filter events catches XIM events and sends them to the correct handler */
|
||||
if (X11_XFilterEvent(xevent, None)) {
|
||||
#if 0
|
||||
printf("Filtered event type = %d display = %d window = %d\n",
|
||||
xevent->type, xevent->xany.display, xevent->xany.window);
|
||||
#endif
|
||||
/* Make sure dead key press/release events are sent */
|
||||
/* But only if we're using one of the DBus IMEs, otherwise
|
||||
some XIM IMEs will generate duplicate events */
|
||||
#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
|
||||
SDL_Scancode scancode = videodata->key_layout[orig_keycode];
|
||||
videodata->filter_code = orig_keycode;
|
||||
videodata->filter_time = xevent->xkey.time;
|
||||
|
||||
if (orig_event_type == KeyPress) {
|
||||
SDL_SendKeyboardKey(0, keyboardID, SDL_PRESSED, scancode);
|
||||
} else {
|
||||
SDL_SendKeyboardKey(0, keyboardID, SDL_RELEASED, scancode);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_XEVENTS
|
||||
printf("window %p: %s (X11 keycode = 0x%X)\n", data, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode);
|
||||
#endif
|
||||
#ifdef DEBUG_SCANCODES
|
||||
if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
|
||||
int min_keycode, max_keycode;
|
||||
X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
|
||||
keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13);
|
||||
SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
|
||||
keycode, keycode - min_keycode, keysym,
|
||||
X11_XKeysymToString(keysym));
|
||||
}
|
||||
#endif /* DEBUG SCANCODES */
|
||||
|
||||
SDL_zeroa(text);
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
if (windowdata->ic && xevent->type == KeyPress) {
|
||||
X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text),
|
||||
&keysym, &status);
|
||||
} else {
|
||||
XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text), &keysym, NULL);
|
||||
}
|
||||
#else
|
||||
XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text), &keysym, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_USE_IME
|
||||
if (SDL_TextInputActive()) {
|
||||
handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode, (xevent->type == KeyPress ? SDL_PRESSED : SDL_RELEASED));
|
||||
}
|
||||
#endif
|
||||
if (!handled_by_ime) {
|
||||
if (xevent->type == KeyPress) {
|
||||
/* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */
|
||||
if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) {
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, videodata->key_layout[keycode]);
|
||||
}
|
||||
if (*text) {
|
||||
SDL_SendKeyboardText(text);
|
||||
}
|
||||
} else {
|
||||
if (X11_KeyRepeat(display, xevent)) {
|
||||
/* We're about to get a repeated key down, ignore the key up */
|
||||
return;
|
||||
}
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, videodata->key_layout[keycode]);
|
||||
}
|
||||
}
|
||||
|
||||
if (xevent->type == KeyPress) {
|
||||
X11_UpdateUserTime(windowdata, xevent->xkey.time);
|
||||
}
|
||||
}
|
||||
|
||||
void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, const float x, const float y, const unsigned long time)
|
||||
{
|
||||
SDL_Window *window = windowdata->window;
|
||||
@ -923,47 +1016,22 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
||||
SDL_VideoData *videodata = _this->driverdata;
|
||||
Display *display;
|
||||
SDL_WindowData *data;
|
||||
int orig_event_type;
|
||||
KeyCode orig_keycode;
|
||||
XClientMessageEvent m;
|
||||
int i;
|
||||
|
||||
SDL_assert(videodata != NULL);
|
||||
display = videodata->display;
|
||||
|
||||
/* Save the original keycode for dead keys, which are filtered out by
|
||||
the XFilterEvent() call below.
|
||||
*/
|
||||
orig_event_type = xevent->type;
|
||||
if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
|
||||
orig_keycode = xevent->xkey.keycode;
|
||||
} else {
|
||||
orig_keycode = 0;
|
||||
}
|
||||
|
||||
/* filter events catches XIM events and sends them to the correct handler */
|
||||
if (X11_XFilterEvent(xevent, None) == True) {
|
||||
/* Key press/release events are filtered in X11_HandleKeyEvent() */
|
||||
if (xevent->type != KeyPress && xevent->type != KeyRelease) {
|
||||
if (X11_XFilterEvent(xevent, None)) {
|
||||
#if 0
|
||||
printf("Filtered event type = %d display = %d window = %d\n",
|
||||
xevent->type, xevent->xany.display, xevent->xany.window);
|
||||
#endif
|
||||
/* Make sure dead key press/release events are sent */
|
||||
/* But only if we're using one of the DBus IMEs, otherwise
|
||||
some XIM IMEs will generate duplicate events */
|
||||
if (orig_keycode) {
|
||||
#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
|
||||
SDL_Scancode scancode = videodata->key_layout[orig_keycode];
|
||||
videodata->filter_code = orig_keycode;
|
||||
videodata->filter_time = xevent->xkey.time;
|
||||
|
||||
if (orig_event_type == KeyPress) {
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, scancode);
|
||||
} else {
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scancode);
|
||||
}
|
||||
printf("Filtered event type = %d display = %d window = %d\n",
|
||||
xevent->type, xevent->xany.display, xevent->xany.window);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
|
||||
@ -1217,69 +1285,6 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
||||
#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
|
||||
} break;
|
||||
|
||||
/* Key press/release? */
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
{
|
||||
KeyCode keycode = xevent->xkey.keycode;
|
||||
KeySym keysym = NoSymbol;
|
||||
char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
||||
Status status = 0;
|
||||
SDL_bool handled_by_ime = SDL_FALSE;
|
||||
|
||||
#ifdef DEBUG_XEVENTS
|
||||
printf("window %p: %s (X11 keycode = 0x%X)\n", data, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode);
|
||||
#endif
|
||||
#ifdef DEBUG_SCANCODES
|
||||
if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
|
||||
int min_keycode, max_keycode;
|
||||
X11_XDisplayKeycodes(display, &min_keycode, &max_keycode);
|
||||
keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13);
|
||||
SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
|
||||
keycode, keycode - min_keycode, keysym,
|
||||
X11_XKeysymToString(keysym));
|
||||
}
|
||||
#endif /* DEBUG SCANCODES */
|
||||
|
||||
SDL_zeroa(text);
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
if (data->ic && xevent->type == KeyPress) {
|
||||
X11_Xutf8LookupString(data->ic, &xevent->xkey, text, sizeof(text),
|
||||
&keysym, &status);
|
||||
} else {
|
||||
XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text), &keysym, NULL);
|
||||
}
|
||||
#else
|
||||
XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text), &keysym, NULL);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_USE_IME
|
||||
if (SDL_TextInputActive()) {
|
||||
handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode, (xevent->type == KeyPress ? SDL_PRESSED : SDL_RELEASED));
|
||||
}
|
||||
#endif
|
||||
if (!handled_by_ime) {
|
||||
if (xevent->type == KeyPress) {
|
||||
/* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */
|
||||
if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) {
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, videodata->key_layout[keycode]);
|
||||
}
|
||||
if (*text) {
|
||||
SDL_SendKeyboardText(text);
|
||||
}
|
||||
} else {
|
||||
if (X11_KeyRepeat(display, xevent)) {
|
||||
/* We're about to get a repeated key down, ignore the key up */
|
||||
break;
|
||||
}
|
||||
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, videodata->key_layout[keycode]);
|
||||
}
|
||||
}
|
||||
|
||||
if (xevent->type == KeyPress) {
|
||||
X11_UpdateUserTime(data, xevent->xkey.time);
|
||||
}
|
||||
} break;
|
||||
|
||||
/* Have we been iconified? */
|
||||
case UnmapNotify:
|
||||
@ -1511,11 +1516,19 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
||||
} break;
|
||||
|
||||
/* Use XInput2 instead of the xevents API if possible, for:
|
||||
- KeyPress
|
||||
- KeyRelease
|
||||
- MotionNotify
|
||||
- ButtonPress
|
||||
- ButtonRelease
|
||||
XInput2 has more precise information, e.g., to distinguish different input devices. */
|
||||
#ifndef SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
case KeyPress:
|
||||
case KeyRelease:
|
||||
{
|
||||
X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent);
|
||||
} break;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
|
@ -29,8 +29,9 @@ extern void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern int X11_SuspendScreenSaver(SDL_VideoDevice *_this);
|
||||
extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this);
|
||||
extern void X11_GetBorderValues(SDL_WindowData *data);
|
||||
extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *wdata, SDL_MouseID mouseID, int button, const float x, const float y, const unsigned long time);
|
||||
extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *wdata, SDL_MouseID mouseID, int button);
|
||||
extern void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent);
|
||||
extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, const float x, const float y, const unsigned long time);
|
||||
extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button);
|
||||
extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window);
|
||||
extern SDL_bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, SDL_bool force_new_result);
|
||||
extern SDL_bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y);
|
||||
|
@ -794,16 +794,17 @@ int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesI
|
||||
X11_Xinput2SelectTouch(_this, window);
|
||||
|
||||
{
|
||||
unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask;
|
||||
unsigned int x11_pointer_events = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
|
||||
if (X11_Xinput2SelectMouse(_this, window)) {
|
||||
/* If XInput2 can handle pointer events, we don't track them here */
|
||||
if (X11_Xinput2SelectMouseAndKeyboard(_this, window)) {
|
||||
/* If XInput2 can handle pointer and keyboard events, we don't track them here */
|
||||
x11_keyboard_events = 0;
|
||||
x11_pointer_events = 0;
|
||||
}
|
||||
|
||||
X11_XSelectInput(display, w,
|
||||
(FocusChangeMask | EnterWindowMask | LeaveWindowMask | ExposureMask |
|
||||
x11_pointer_events |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
x11_keyboard_events | x11_pointer_events |
|
||||
PropertyChangeMask | StructureNotifyMask |
|
||||
KeymapStateMask | fevent));
|
||||
}
|
||||
|
@ -423,6 +423,41 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
|
||||
X11_InitPen(_this);
|
||||
} break;
|
||||
|
||||
case XI_KeyPress:
|
||||
case XI_KeyRelease:
|
||||
{
|
||||
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
|
||||
SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
|
||||
XEvent xevent;
|
||||
|
||||
if (xev->deviceid != xev->sourceid) {
|
||||
/* Discard events from "Master" devices to avoid duplicates. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (cookie->evtype == XI_KeyPress) {
|
||||
xevent.type = KeyPress;
|
||||
} else {
|
||||
xevent.type = KeyRelease;
|
||||
}
|
||||
xevent.xkey.serial = xev->serial;
|
||||
xevent.xkey.send_event = xev->send_event;
|
||||
xevent.xkey.display = xev->display;
|
||||
xevent.xkey.window = xev->event;
|
||||
xevent.xkey.root = xev->root;
|
||||
xevent.xkey.subwindow = xev->child;
|
||||
xevent.xkey.time = xev->time;
|
||||
xevent.xkey.x = xev->event_x;
|
||||
xevent.xkey.y = xev->event_y;
|
||||
xevent.xkey.x_root = xev->root_x;
|
||||
xevent.xkey.y_root = xev->root_y;
|
||||
xevent.xkey.state = xev->mods.effective;
|
||||
xevent.xkey.keycode = xev->detail;
|
||||
xevent.xkey.same_screen = 1;
|
||||
|
||||
X11_HandleKeyEvent(_this, windowdata, (SDL_KeyboardID)xev->sourceid, &xevent);
|
||||
} break;
|
||||
|
||||
case XI_RawButtonPress:
|
||||
case XI_RawButtonRelease:
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
|
||||
@ -608,7 +643,7 @@ int X11_Xinput2IsInitialized(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_bool X11_Xinput2SelectMouse(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
SDL_bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
|
||||
const SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
|
||||
@ -620,6 +655,8 @@ SDL_bool X11_Xinput2SelectMouse(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
eventmask.mask = mask;
|
||||
eventmask.deviceid = XIAllDevices;
|
||||
|
||||
XISetMask(mask, XI_KeyPress);
|
||||
XISetMask(mask, XI_KeyRelease);
|
||||
XISetMask(mask, XI_ButtonPress);
|
||||
XISetMask(mask, XI_ButtonRelease);
|
||||
XISetMask(mask, XI_Motion);
|
||||
|
@ -38,6 +38,6 @@ extern int X11_Xinput2IsMultitouchSupported(void);
|
||||
extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern SDL_bool X11_Xinput2SelectMouse(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern SDL_bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
|
||||
#endif /* SDL_x11xinput2_h_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user