Fixed XCB events being handled by the wrong windows in multi-window applications (#843).

This commit is contained in:
binary1248 2015-03-25 22:08:50 +01:00 committed by Lukas Dürrenberger
parent c229877313
commit bd34935f2a
2 changed files with 145 additions and 32 deletions

View File

@ -31,6 +31,8 @@
#include <SFML/Window/Unix/ScopedXcbPtr.hpp> #include <SFML/Window/Unix/ScopedXcbPtr.hpp>
#include <SFML/System/Utf.hpp> #include <SFML/System/Utf.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
#include <xcb/xcb_image.h> #include <xcb/xcb_image.h>
#include <xcb/randr.h> #include <xcb/randr.h>
@ -55,6 +57,7 @@ namespace
{ {
sf::priv::WindowImplX11* fullscreenWindow = NULL; sf::priv::WindowImplX11* fullscreenWindow = NULL;
std::vector<sf::priv::WindowImplX11*> allWindows; std::vector<sf::priv::WindowImplX11*> allWindows;
sf::Mutex allWindowsMutex;
unsigned long eventMask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS | 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_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION |
XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEY_PRESS |
@ -429,6 +432,7 @@ WindowImplX11::~WindowImplX11()
CloseDisplay(m_display); CloseDisplay(m_display);
// Remove this window from the global list of windows (required for focus request) // 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)); allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this));
} }
@ -453,7 +457,17 @@ void WindowImplX11::processEvents()
xcb_key_release_event_t* lastKeyReleaseEvent = NULL; xcb_key_release_event_t* lastKeyReleaseEvent = NULL;
uint8_t eventType = 0; 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; 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 there's still a key release event held back, process it now.
if (lastKeyReleaseEvent) if (lastKeyReleaseEvent)
{ {
processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)); if (processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)))
free(lastKeyReleaseEvent); free(lastKeyReleaseEvent);
lastKeyReleaseEvent = NULL; lastKeyReleaseEvent = NULL;
} }
if (eventType == XCB_KEY_RELEASE) if (eventType == XCB_KEY_RELEASE)
{
// Check if we're in charge of the key release
if (!passEvent(event, reinterpret_cast<xcb_key_release_event_t*>(event)->event))
{ {
// Remember this key release event. // Remember this key release event.
lastKeyReleaseEvent = reinterpret_cast<xcb_key_release_event_t*>(event); lastKeyReleaseEvent = reinterpret_cast<xcb_key_release_event_t*>(event);
event = NULL; // For safety reasons. event = NULL; // For safety reasons.
} }
}
else else
{ {
processEvent(event); if (processEvent(event))
free(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. // Process any held back release event.
if (lastKeyReleaseEvent) if (lastKeyReleaseEvent)
{ {
processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)); if (processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)))
free(lastKeyReleaseEvent); free(lastKeyReleaseEvent);
lastKeyReleaseEvent = NULL; lastKeyReleaseEvent = NULL;
} }
} }
@ -917,6 +947,9 @@ void WindowImplX11::requestFocus()
// Check the global list of windows to find out whether an SFML window has the focus // 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. // Note: can't handle console and other non-SFML windows belonging to the application.
bool sfmlWindowFocused = false; bool sfmlWindowFocused = false;
{
Lock lock(allWindowsMutex);
for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr)
{ {
if ((*itr)->hasFocus()) if ((*itr)->hasFocus())
@ -925,6 +958,7 @@ void WindowImplX11::requestFocus()
break; break;
} }
} }
}
// Check if window is viewable (not on other desktop, ...) // Check if window is viewable (not on other desktop, ...)
// TODO: Check also if minimized // TODO: Check also if minimized
@ -1492,6 +1526,7 @@ void WindowImplX11::initialize()
xcb_flush(m_connection); xcb_flush(m_connection);
// Add this window to the global list of windows (required for focus request) // Add this window to the global list of windows (required for focus request)
Lock lock(allWindowsMutex);
allWindows.push_back(this); allWindows.push_back(this);
} }
@ -1574,6 +1609,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Destroy event // Destroy event
case XCB_DESTROY_NOTIFY: case XCB_DESTROY_NOTIFY:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_destroy_notify_event_t*>(windowEvent)->window))
return false;
// The window is about to be destroyed: we must cleanup resources // The window is about to be destroyed: we must cleanup resources
cleanup(); cleanup();
break; break;
@ -1582,6 +1620,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Gain focus event // Gain focus event
case XCB_FOCUS_IN: case XCB_FOCUS_IN:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_focus_in_event_t*>(windowEvent)->event))
return false;
// Update the input context // Update the input context
if (m_inputContext) if (m_inputContext)
XSetICFocus(m_inputContext); XSetICFocus(m_inputContext);
@ -1632,6 +1673,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Lost focus event // Lost focus event
case XCB_FOCUS_OUT: case XCB_FOCUS_OUT:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_focus_out_event_t*>(windowEvent)->event))
return false;
// Update the input context // Update the input context
if (m_inputContext) if (m_inputContext)
XUnsetICFocus(m_inputContext); XUnsetICFocus(m_inputContext);
@ -1645,6 +1689,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Resize event // Resize event
case XCB_CONFIGURE_NOTIFY: case XCB_CONFIGURE_NOTIFY:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_configure_notify_event_t*>(windowEvent)->window))
return false;
xcb_configure_notify_event_t* e = reinterpret_cast<xcb_configure_notify_event_t*>(windowEvent); xcb_configure_notify_event_t* e = reinterpret_cast<xcb_configure_notify_event_t*>(windowEvent);
Event event; Event event;
event.type = Event::Resized; event.type = Event::Resized;
@ -1657,6 +1704,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Close event // Close event
case XCB_CLIENT_MESSAGE: case XCB_CLIENT_MESSAGE:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_client_message_event_t*>(windowEvent)->window))
return false;
xcb_client_message_event_t* e = reinterpret_cast<xcb_client_message_event_t*>(windowEvent); xcb_client_message_event_t* e = reinterpret_cast<xcb_client_message_event_t*>(windowEvent);
// Handle window manager protocol messages we support // Handle window manager protocol messages we support
@ -1695,6 +1745,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Key down event // Key down event
case XCB_KEY_PRESS: case XCB_KEY_PRESS:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_key_press_event_t*>(windowEvent)->event))
return false;
xcb_key_press_event_t* e = reinterpret_cast<xcb_key_press_event_t*>(windowEvent); xcb_key_press_event_t* e = reinterpret_cast<xcb_key_press_event_t*>(windowEvent);
// Get the keysym of the key that has been pressed // 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 // Key up event
case XCB_KEY_RELEASE: case XCB_KEY_RELEASE:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_key_release_event_t*>(windowEvent)->event))
return false;
xcb_key_release_event_t* e = reinterpret_cast<xcb_key_release_event_t*>(windowEvent); xcb_key_release_event_t* e = reinterpret_cast<xcb_key_release_event_t*>(windowEvent);
// Get the keysym of the key that has been pressed // 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 // Mouse button pressed
case XCB_BUTTON_PRESS: case XCB_BUTTON_PRESS:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_button_press_event_t*>(windowEvent)->event))
return false;
xcb_button_press_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(windowEvent); xcb_button_press_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(windowEvent);
// XXX: Why button 8 and 9? // XXX: Why button 8 and 9?
@ -1837,6 +1896,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Mouse button released // Mouse button released
case XCB_BUTTON_RELEASE: case XCB_BUTTON_RELEASE:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_button_release_event_t*>(windowEvent)->event))
return false;
xcb_button_release_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(windowEvent); xcb_button_release_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(windowEvent);
xcb_button_t button = e->detail; xcb_button_t button = e->detail;
@ -1891,6 +1953,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Mouse moved // Mouse moved
case XCB_MOTION_NOTIFY: case XCB_MOTION_NOTIFY:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_motion_notify_event_t*>(windowEvent)->event))
return false;
xcb_motion_notify_event_t* e = reinterpret_cast<xcb_motion_notify_event_t*>(windowEvent); xcb_motion_notify_event_t* e = reinterpret_cast<xcb_motion_notify_event_t*>(windowEvent);
Event event; Event event;
event.type = Event::MouseMoved; event.type = Event::MouseMoved;
@ -1903,6 +1968,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Mouse entered // Mouse entered
case XCB_ENTER_NOTIFY: case XCB_ENTER_NOTIFY:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_enter_notify_event_t*>(windowEvent)->event))
return false;
xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast<xcb_enter_notify_event_t*>(windowEvent); xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast<xcb_enter_notify_event_t*>(windowEvent);
if (enterNotifyEvent->mode == NotifyNormal) if (enterNotifyEvent->mode == NotifyNormal)
@ -1917,6 +1985,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Mouse left // Mouse left
case XCB_LEAVE_NOTIFY: case XCB_LEAVE_NOTIFY:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_leave_notify_event_t*>(windowEvent)->event))
return false;
xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast<xcb_leave_notify_event_t*>(windowEvent); xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast<xcb_leave_notify_event_t*>(windowEvent);
if (leaveNotifyEvent->mode == NotifyNormal) if (leaveNotifyEvent->mode == NotifyNormal)
@ -1931,6 +2002,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Parent window changed // Parent window changed
case XCB_REPARENT_NOTIFY: case XCB_REPARENT_NOTIFY:
{ {
if (passEvent(windowEvent, reinterpret_cast<xcb_reparent_notify_event_t*>(windowEvent)->window))
return false;
// Catch reparent events to properly apply fullscreen on // Catch reparent events to properly apply fullscreen on
// some "strange" window managers (like Awesome) which // some "strange" window managers (like Awesome) which
// seem to make use of temporary parents during mapping // 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<sf::priv::WindowImplX11*>::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) Keyboard::Key WindowImplX11::keysymToSF(xcb_keysym_t symbol)
{ {

View File

@ -33,7 +33,7 @@
#include <SFML/System/String.hpp> #include <SFML/System/String.hpp>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <xcb/xcb_ewmh.h> #include <xcb/xcb_ewmh.h>
#include <set> #include <deque>
namespace sf namespace sf
@ -246,6 +246,17 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
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 /// \brief Convert a X11 keysym to SFML key code
/// ///
@ -259,13 +270,14 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
::Window m_window; ///< X11 structure defining our window xcb_window_t m_window; ///< xcb identifier defining our window
::Display* m_display; ///< Pointer to the display ::Display* m_display; ///< Pointer to the display
xcb_connection_t* m_connection; ///< Pointer to the xcb connection xcb_connection_t* m_connection; ///< Pointer to the xcb connection
xcb_ewmh_connection_t m_ewmhConnection; ///< xcb EWMH connection xcb_ewmh_connection_t m_ewmhConnection; ///< xcb EWMH connection
xcb_screen_t* m_screen; ///< Screen identifier xcb_screen_t* m_screen; ///< Screen identifier
XIM m_inputMethod; ///< Input method linked to the X display XIM m_inputMethod; ///< Input method linked to the X display
XIC m_inputContext; ///< Input context used to get unicode input in our window XIC m_inputContext; ///< Input context used to get unicode input in our window
std::deque<xcb_generic_event_t*> 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 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_atomWmProtocols; ///< Atom used to identify WM protocol messages
Atom m_atomClose; ///< Atom used to identify the close event Atom m_atomClose; ///< Atom used to identify the close event