From 44944989e86cc96e7b5ff818cafacc5210abd435 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Mon, 25 Sep 2017 13:38:58 +0200 Subject: [PATCH] Fixed X11 key repeat handling not filtering out events from other windows. (Fixes #1223) --- src/SFML/Window/Unix/WindowImplX11.cpp | 57 +++++++++++++++++--------- src/SFML/Window/Unix/WindowImplX11.hpp | 37 +++++++++-------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 94af569a4..83925a081 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -77,6 +77,21 @@ namespace static const unsigned int maxTrialsCount = 5; + // Predicate we use to find key repeat events in processEvent + struct KeyRepeatFinder + { + KeyRepeatFinder(unsigned int keycode, Time time) : keycode(keycode), time(time) {} + + // 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) { @@ -752,8 +767,16 @@ WindowHandle WindowImplX11::getSystemHandle() const void WindowImplX11::processEvents() { XEvent event; + + // 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); } } @@ -1533,29 +1556,23 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) // - Discard both duplicated KeyPress and KeyRelease events when KeyRepeatEnabled is false // Detect repeated key events - // (code shamelessly taken from SDL) if (windowEvent.type == KeyRelease) { - // Check if there's a matching KeyPress event in the queue - XEvent nextEvent; - if (XPending(m_display)) - { - // Grab it but don't remove it from the queue, it still needs to be processed :) - XPeekEvent(m_display, &nextEvent); - if (nextEvent.type == KeyPress) - { - // Check if it is a duplicated event (same timestamp as the KeyRelease event) - if ((nextEvent.xkey.keycode == windowEvent.xkey.keycode) && - (nextEvent.xkey.time - windowEvent.xkey.time < 2)) - { - // If we don't want repeated events, remove the next KeyPress from the queue - if (!m_keyRepeat) - XNextEvent(m_display, &nextEvent); + // 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) + ); - // This KeyRelease is a repeated event and we don't want it - return false; - } - } + 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; } } diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 13596f82f..417471452 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -267,24 +267,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 - bool m_isExternal; ///< Tell whether the window has been created externally or by SFML - int m_oldVideoMode; ///< Video mode 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 + 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 + int m_oldVideoMode; ///< Video mode 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