From bd34935f2a0526ee35c5bb277c98f9089b5fb177 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Wed, 25 Mar 2015 22:08:50 +0100 Subject: [PATCH] Fixed XCB events being handled by the wrong windows in multi-window applications (#843). --- src/SFML/Window/Unix/WindowImplX11.cpp | 129 ++++++++++++++++++++++--- src/SFML/Window/Unix/WindowImplX11.hpp | 48 +++++---- 2 files changed, 145 insertions(+), 32 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index a04b42a6..b826b70a 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -55,6 +57,7 @@ namespace { sf::priv::WindowImplX11* fullscreenWindow = NULL; std::vector allWindows; + sf::Mutex allWindowsMutex; unsigned long eventMask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEY_PRESS | @@ -429,6 +432,7 @@ WindowImplX11::~WindowImplX11() CloseDisplay(m_display); // Remove this window from the global list of windows (required for focus request) + Lock lock(allWindowsMutex); allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this)); } @@ -453,7 +457,17 @@ void WindowImplX11::processEvents() xcb_key_release_event_t* lastKeyReleaseEvent = NULL; uint8_t eventType = 0; - while((event = xcb_poll_for_event(m_connection))) + if (!m_xcbEvents.empty()) + { + event = m_xcbEvents.front(); + m_xcbEvents.pop_front(); + } + else + { + event = xcb_poll_for_event(m_connection); + } + + while (event) { eventType = event->response_type & ~0x80; @@ -474,29 +488,45 @@ void WindowImplX11::processEvents() // If there's still a key release event held back, process it now. if (lastKeyReleaseEvent) { - processEvent(reinterpret_cast(lastKeyReleaseEvent)); - free(lastKeyReleaseEvent); + if (processEvent(reinterpret_cast(lastKeyReleaseEvent))) + free(lastKeyReleaseEvent); + lastKeyReleaseEvent = NULL; } if (eventType == XCB_KEY_RELEASE) { - // Remember this key release event. - lastKeyReleaseEvent = reinterpret_cast(event); - event = NULL; // For safety reasons. + // Check if we're in charge of the key release + if (!passEvent(event, reinterpret_cast(event)->event)) + { + // Remember this key release event. + lastKeyReleaseEvent = reinterpret_cast(event); + event = NULL; // For safety reasons. + } } else { - processEvent(event); - free(event); + if (processEvent(event)) + free(event); + } + + if (!m_xcbEvents.empty()) + { + event = m_xcbEvents.front(); + m_xcbEvents.pop_front(); + } + else + { + event = xcb_poll_for_event(m_connection); } } // Process any held back release event. if (lastKeyReleaseEvent) { - processEvent(reinterpret_cast(lastKeyReleaseEvent)); - free(lastKeyReleaseEvent); + if (processEvent(reinterpret_cast(lastKeyReleaseEvent))) + free(lastKeyReleaseEvent); + lastKeyReleaseEvent = NULL; } } @@ -917,12 +947,16 @@ void WindowImplX11::requestFocus() // Check the global list of windows to find out whether an SFML window has the focus // Note: can't handle console and other non-SFML windows belonging to the application. bool sfmlWindowFocused = false; - for (std::vector::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) + { - if ((*itr)->hasFocus()) + Lock lock(allWindowsMutex); + for (std::vector::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) { - sfmlWindowFocused = true; - break; + if ((*itr)->hasFocus()) + { + sfmlWindowFocused = true; + break; + } } } @@ -1492,6 +1526,7 @@ void WindowImplX11::initialize() xcb_flush(m_connection); // Add this window to the global list of windows (required for focus request) + Lock lock(allWindowsMutex); allWindows.push_back(this); } @@ -1574,6 +1609,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Destroy event case XCB_DESTROY_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + // The window is about to be destroyed: we must cleanup resources cleanup(); break; @@ -1582,6 +1620,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Gain focus event case XCB_FOCUS_IN: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + // Update the input context if (m_inputContext) XSetICFocus(m_inputContext); @@ -1632,6 +1673,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Lost focus event case XCB_FOCUS_OUT: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + // Update the input context if (m_inputContext) XUnsetICFocus(m_inputContext); @@ -1645,6 +1689,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Resize event case XCB_CONFIGURE_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + xcb_configure_notify_event_t* e = reinterpret_cast(windowEvent); Event event; event.type = Event::Resized; @@ -1657,6 +1704,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Close event case XCB_CLIENT_MESSAGE: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + xcb_client_message_event_t* e = reinterpret_cast(windowEvent); // Handle window manager protocol messages we support @@ -1695,6 +1745,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Key down event case XCB_KEY_PRESS: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_key_press_event_t* e = reinterpret_cast(windowEvent); // Get the keysym of the key that has been pressed @@ -1776,6 +1829,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Key up event case XCB_KEY_RELEASE: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_key_release_event_t* e = reinterpret_cast(windowEvent); // Get the keysym of the key that has been pressed @@ -1806,6 +1862,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse button pressed case XCB_BUTTON_PRESS: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_button_press_event_t* e = reinterpret_cast(windowEvent); // XXX: Why button 8 and 9? @@ -1837,6 +1896,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse button released case XCB_BUTTON_RELEASE: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_button_release_event_t* e = reinterpret_cast(windowEvent); xcb_button_t button = e->detail; @@ -1891,6 +1953,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse moved case XCB_MOTION_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_motion_notify_event_t* e = reinterpret_cast(windowEvent); Event event; event.type = Event::MouseMoved; @@ -1903,6 +1968,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse entered case XCB_ENTER_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast(windowEvent); if (enterNotifyEvent->mode == NotifyNormal) @@ -1917,6 +1985,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse left case XCB_LEAVE_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast(windowEvent); if (leaveNotifyEvent->mode == NotifyNormal) @@ -1931,6 +2002,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Parent window changed case XCB_REPARENT_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + // Catch reparent events to properly apply fullscreen on // some "strange" window managers (like Awesome) which // seem to make use of temporary parents during mapping @@ -1946,6 +2020,33 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) } +//////////////////////////////////////////////////////////// +bool WindowImplX11::passEvent(xcb_generic_event_t* windowEvent, xcb_window_t window) +{ + // Check if this is our event + if (window == m_window) + return false; + + Lock lock(allWindowsMutex); + + // If we are the only window, there is nobody else to pass to + if (allWindows.size() == 1) + return false; + + for (std::vector::iterator i = allWindows.begin(); i != allWindows.end(); ++i) + { + if ((*i)->m_window == window) + { + (*i)->m_xcbEvents.push_back(windowEvent); + return true; + } + } + + // It seems nobody wants the event, we'll just handle it ourselves + return false; +} + + //////////////////////////////////////////////////////////// Keyboard::Key WindowImplX11::keysymToSF(xcb_keysym_t symbol) { diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 575942c8..b3918072 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include namespace sf @@ -244,7 +244,18 @@ private: /// \return True if the event was processed, false if it was discarded /// //////////////////////////////////////////////////////////// - bool processEvent(xcb_generic_event_t *windowEvent); + bool processEvent(xcb_generic_event_t* windowEvent); + + //////////////////////////////////////////////////////////// + /// \brief Pass an incoming event to another window + /// + /// \param windowEvent Event which is being processed + /// \param window Window to pass to + /// + /// \return True if the event was passed to another window, false if it is destined for the current window + /// + //////////////////////////////////////////////////////////// + bool passEvent(xcb_generic_event_t* windowEvent, xcb_window_t window); //////////////////////////////////////////////////////////// /// \brief Convert a X11 keysym to SFML key code @@ -259,22 +270,23 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - ::Window m_window; ///< X11 structure defining our window - ::Display* m_display; ///< Pointer to the display - xcb_connection_t* m_connection; ///< Pointer to the xcb connection - xcb_ewmh_connection_t m_ewmhConnection; ///< xcb EWMH connection - xcb_screen_t* 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 - Atom m_atomWmProtocols; ///< Atom used to identify WM protocol messages - Atom m_atomClose; ///< Atom used to identify the close event - int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen - Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one - 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 window in fullscreen? + xcb_window_t m_window; ///< xcb identifier defining our window + ::Display* m_display; ///< Pointer to the display + xcb_connection_t* m_connection; ///< Pointer to the xcb connection + xcb_ewmh_connection_t m_ewmhConnection; ///< xcb EWMH connection + xcb_screen_t* 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_xcbEvents; ///< Events that were received in another window's loop + bool m_isExternal; ///< Tell whether the window has been created externally or by SFML + Atom m_atomWmProtocols; ///< Atom used to identify WM protocol messages + Atom m_atomClose; ///< Atom used to identify the close event + int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one + 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 window in fullscreen? }; } // namespace priv