diff --git a/src/SFML/Window/Linux/WindowImplX11.cpp b/src/SFML/Window/Linux/WindowImplX11.cpp index 590f90af5..e63763d27 100644 --- a/src/SFML/Window/Linux/WindowImplX11.cpp +++ b/src/SFML/Window/Linux/WindowImplX11.cpp @@ -362,30 +362,53 @@ void WindowImplX11::Display() //////////////////////////////////////////////////////////// void WindowImplX11::ProcessEvents() { + // 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 EnableKeyRepeat is true + // - Discard both duplicated KeyPress and KeyRelease events when EnableKeyRepeat is false + + // Process any event in the queue matching our window XEvent Event; - while (XCheckIfEvent(ourDisplay, &Event, &WindowImplX11::CheckEvent, reinterpret_cast(myWindow))) + while (XCheckWindowEvent(ourDisplay, myWindow, ourEventMask, &Event)) { - // Filter repeated key events - if (Event.type == KeyRelease) + // Detect repeated key events + if ((Event.type == KeyPress) || (Event.type == KeyRelease)) { - if (XPending(ourDisplay)) + if (Event.xkey.keycode < 256) { - XEvent NextEvent; - XPeekEvent(ourDisplay, &NextEvent); - if ((NextEvent.type == KeyPress) && - (NextEvent.xkey.keycode == Event.xkey.keycode) && - (NextEvent.xkey.time == Event.xkey.time)) + // To detect if it is a repeated key event, we check the current state of the key. + // - If the state is "down", KeyReleased events must obviously be discarded. + // - KeyPress events are a little bit harder to handle: they depend on the EnableKeyRepeat state, + // and we need to properly forward the first one. + char Keys[32]; + XQueryKeymap(ourDisplay, Keys); + if (Keys[Event.xkey.keycode >> 3] & (1 << (Event.xkey.keycode % 8))) { - if (!myKeyRepeat) - XNextEvent(ourDisplay, &NextEvent); - continue; + // KeyRelease event + key down = repeated event --> discard + if (Event.type == KeyRelease) + { + myLastKeyReleaseEvent = Event; + continue; + } + + // KeyPress event + key repeat disabled + matching KeyRelease event = repeated event --> discard + if ((Event.type == KeyPress) && !myKeyRepeat && + (myLastKeyReleaseEvent.xkey.keycode == Event.xkey.keycode) && + (myLastKeyReleaseEvent.xkey.time == Event.xkey.time)) + { + continue; + } } } } + // Process the event ProcessEvent(Event); - } + } } @@ -709,6 +732,9 @@ bool WindowImplX11::CreateContext(const VideoMode& Mode, XVisualInfo& ChosenVisu //////////////////////////////////////////////////////////// void WindowImplX11::Initialize() { + // Make sure the "last key release" is initialized with invalid values + myLastKeyReleaseEvent.type = -1; + // Get the atom defining the close event myAtomClose = XInternAtom(ourDisplay, "WM_DELETE_WINDOW", false); XSetWMProtocols(ourDisplay, myWindow, &myAtomClose, 1); @@ -802,17 +828,6 @@ void WindowImplX11::CleanUp() } -//////////////////////////////////////////////////////////// -/// Filter the received events -/// (only allow those matching a specific window) -//////////////////////////////////////////////////////////// -Bool WindowImplX11::CheckEvent(::Display*, XEvent* Event, XPointer UserData) -{ - // Just check if the event matches our window - return Event->xany.window == reinterpret_cast< ::Window >(UserData); -} - - //////////////////////////////////////////////////////////// /// Process an incoming event from the window //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/Linux/WindowImplX11.hpp b/src/SFML/Window/Linux/WindowImplX11.hpp index b4506fced..12f4f0450 100644 --- a/src/SFML/Window/Linux/WindowImplX11.hpp +++ b/src/SFML/Window/Linux/WindowImplX11.hpp @@ -200,18 +200,6 @@ private : //////////////////////////////////////////////////////////// void CleanUp(); - //////////////////////////////////////////////////////////// - /// Filter the received events - /// (only allow those matching a specific window) - /// - /// \param Event : Event to filter - /// \param UserData : Data passed to the function (here : the window to compare) - /// - /// \return True if the event belongs to the specified window - /// - //////////////////////////////////////////////////////////// - static Bool CheckEvent(::Display*, XEvent* Event, XPointer UserData); - //////////////////////////////////////////////////////////// /// Process an incoming event from the window /// @@ -259,14 +247,15 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - ::Window myWindow; ///< X11 structure defining our window - bool myIsExternal; ///< Tell whether the window has been created externally or by SFML - GLXContext myGLContext; ///< OpenGL context attached to the window - Atom myAtomClose; ///< Atom used to identify the close event - int myOldVideoMode; ///< Video mode in use before we switch to fullscreen - Cursor myHiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one - XIC myInputContext; ///< Input context used to get unicode input in our window - bool myKeyRepeat; ///< Is the KeyRepeat feature enabled ? + ::Window myWindow; ///< X11 structure defining our window + bool myIsExternal; ///< Tell whether the window has been created externally or by SFML + GLXContext myGLContext; ///< OpenGL context attached to the window + Atom myAtomClose; ///< Atom used to identify the close event + int myOldVideoMode; ///< Video mode in use before we switch to fullscreen + Cursor myHiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one + XIC myInputContext; ///< Input context used to get unicode input in our window + bool myKeyRepeat; ///< Is the KeyRepeat feature enabled ? + XEvent myLastKeyReleaseEvent; ///< Last key release event we received (needed for discarding repeated key events) }; } // namespace priv