From f796205a2173f8dfbf75b0a19b3a2de64796cdb8 Mon Sep 17 00:00:00 2001 From: kimci86 Date: Tue, 11 Oct 2022 22:46:46 +0200 Subject: [PATCH] Process events in order to avoid X11 input method being confused --- src/SFML/Window/Unix/WindowImplX11.cpp | 105 +++++++++++++------------ src/SFML/Window/Unix/WindowImplX11.hpp | 39 +++++---- 2 files changed, 73 insertions(+), 71 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index b78548717..fb2a6ff77 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -84,21 +84,6 @@ namespace static const unsigned int maxTrialsCount = 5; - // Predicate we use to find key repeat events in processEvent - struct KeyRepeatFinder - { - KeyRepeatFinder(unsigned int initalKeycode, Time initialTime) : keycode(initalKeycode), time(initialTime) {} - - // Predicate operator that checks event type, keycode and timestamp - bool operator()(const XEvent& event) - { - return ((event.type == KeyPress) && (event.xkey.keycode == keycode) && (event.xkey.time - time < 2)); - } - - unsigned int keycode; - Time time; - }; - // Filter the events received by windows (only allow those matching a specific window) Bool checkEvent(::Display*, XEvent* event, XPointer userData) { @@ -834,14 +819,61 @@ void WindowImplX11::processEvents() // Pick out the events that are interesting for this window while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast(m_window))) - m_events.push_back(event); - - // Handle the events for this window that we just picked out - while (!m_events.empty()) { - event = m_events.front(); - m_events.pop_front(); - processEvent(event); + // This function implements a workaround to properly discard + // repeated key events when necessary. The problem is that the + // system's key events policy doesn't match SFML's one: X server will generate + // both repeated KeyPress and KeyRelease events when maintaining a key down, while + // SFML only wants repeated KeyPress events. Thus, we have to: + // - Discard duplicated KeyRelease events when m_keyRepeat is true + // - Discard both duplicated KeyPress and KeyRelease events when m_keyRepeat is false + + bool processThisEvent = true; + + // Detect repeated key events + while (event.type == KeyRelease) + { + XEvent nextEvent; + if (XCheckIfEvent(m_display, &nextEvent, checkEvent, reinterpret_cast(m_window))) + { + if ((nextEvent.type == KeyPress) && (nextEvent.xkey.keycode == event.xkey.keycode) && + (event.xkey.time <= nextEvent.xkey.time) && (nextEvent.xkey.time <= event.xkey.time + 1)) + { + // This sequence of events comes from maintaining a key down + if (m_keyRepeat) + { + // Ignore the KeyRelease event and process the KeyPress event + event = nextEvent; + break; + } + else + { + // Ignore both events + processThisEvent = false; + break; + } + } + else + { + // This sequence of events does not come from maintaining a key down, + // so process the KeyRelease event normally, + processEvent(event); + // but loop because the next event can be the first half + // of a sequence coming from maintaining a key down. + event = nextEvent; + } + } + else + { + // No event after this KeyRelease event so assume it can be processed. + break; + } + } + + if (processThisEvent) + { + processEvent(event); + } } // Process clipboard window events @@ -1725,35 +1757,6 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) { using namespace WindowsImplX11Impl; - // This function implements a workaround to properly discard - // repeated key events when necessary. The problem is that the - // system's key events policy doesn't match SFML's one: X server will generate - // both repeated KeyPress and KeyRelease events when maintaining a key down, while - // SFML only wants repeated KeyPress events. Thus, we have to: - // - Discard duplicated KeyRelease events when KeyRepeatEnabled is true - // - Discard both duplicated KeyPress and KeyRelease events when KeyRepeatEnabled is false - - // Detect repeated key events - if (windowEvent.type == KeyRelease) - { - // Find the next KeyPress event with matching keycode and time - std::deque::iterator iter = std::find_if( - m_events.begin(), - m_events.end(), - KeyRepeatFinder(windowEvent.xkey.keycode, windowEvent.xkey.time) - ); - - if (iter != m_events.end()) - { - // If we don't want repeated events, remove the next KeyPress from the queue - if (!m_keyRepeat) - m_events.erase(iter); - - // This KeyRelease is a repeated event and we don't want it - return false; - } - } - // Convert the X11 event to a sf::Event switch (windowEvent.type) { diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index a998d8d60..aca1cd04f 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -300,26 +300,25 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - ::Window m_window; ///< X identifier defining our window - ::Display* m_display; ///< Pointer to the display - int m_screen; ///< Screen identifier - XIM m_inputMethod; ///< Input method linked to the X display - XIC m_inputContext; ///< Input context used to get unicode input in our window - std::deque m_events; ///< Queue we use to store pending events for this window - bool m_isExternal; ///< Tell whether the window has been created externally or by SFML - RRMode m_oldVideoMode; ///< Video mode in use before we switch to fullscreen - RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen - ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one - ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid - bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? - Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only) - bool m_useSizeHints; ///< Is the size of the window fixed with size hints? - bool m_fullscreen; ///< Is the window in fullscreen? - bool m_cursorGrabbed; ///< Is the mouse cursor trapped? - bool m_windowMapped; ///< Has the window been mapped by the window manager? - Pixmap m_iconPixmap; ///< The current icon pixmap if in use - Pixmap m_iconMaskPixmap; ///< The current icon mask pixmap if in use - ::Time m_lastInputTime; ///< Last time we received user input + ::Window m_window; ///< X identifier defining our window + ::Display* m_display; ///< Pointer to the display + int m_screen; ///< Screen identifier + XIM m_inputMethod; ///< Input method linked to the X display + XIC m_inputContext; ///< Input context used to get unicode input in our window + bool m_isExternal; ///< Tell whether the window has been created externally or by SFML + RRMode m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen + ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one + ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid + bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? + Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only) + bool m_useSizeHints; ///< Is the size of the window fixed with size hints? + bool m_fullscreen; ///< Is the window in fullscreen? + bool m_cursorGrabbed; ///< Is the mouse cursor trapped? + bool m_windowMapped; ///< Has the window been mapped by the window manager? + Pixmap m_iconPixmap; ///< The current icon pixmap if in use + Pixmap m_iconMaskPixmap; ///< The current icon mask pixmap if in use + ::Time m_lastInputTime; ///< Last time we received user input }; } // namespace priv