diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 72ad194c6..6d568a90d 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -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 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 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(); diff --git a/src/video/x11/SDL_x11events.h b/src/video/x11/SDL_x11events.h index 911d9102e..4a95a558a 100644 --- a/src/video/x11/SDL_x11events.h +++ b/src/video/x11/SDL_x11events.h @@ -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); diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index dfd9975ec..f056abb2d 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -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)); } diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index 03bb87cd2..e599e0e5c 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -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); diff --git a/src/video/x11/SDL_x11xinput2.h b/src/video/x11/SDL_x11xinput2.h index e67e1cb4c..b0f749fa5 100644 --- a/src/video/x11/SDL_x11xinput2.h +++ b/src/video/x11/SDL_x11xinput2.h @@ -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_ */