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/System/Utf.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_image.h>
#include <xcb/randr.h>
@ -55,6 +57,7 @@ namespace
{
sf::priv::WindowImplX11* fullscreenWindow = NULL;
std::vector<sf::priv::WindowImplX11*> 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<xcb_generic_event_t*>(lastKeyReleaseEvent));
if (processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)))
free(lastKeyReleaseEvent);
lastKeyReleaseEvent = NULL;
}
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.
lastKeyReleaseEvent = reinterpret_cast<xcb_key_release_event_t*>(event);
event = NULL; // For safety reasons.
}
}
else
{
processEvent(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<xcb_generic_event_t*>(lastKeyReleaseEvent));
if (processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)))
free(lastKeyReleaseEvent);
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
// Note: can't handle console and other non-SFML windows belonging to the application.
bool sfmlWindowFocused = false;
{
Lock lock(allWindowsMutex);
for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr)
{
if ((*itr)->hasFocus())
@ -925,6 +958,7 @@ void WindowImplX11::requestFocus()
break;
}
}
}
// Check if window is viewable (not on other desktop, ...)
// TODO: Check also if minimized
@ -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<xcb_destroy_notify_event_t*>(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<xcb_focus_in_event_t*>(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<xcb_focus_out_event_t*>(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<xcb_configure_notify_event_t*>(windowEvent)->window))
return false;
xcb_configure_notify_event_t* e = reinterpret_cast<xcb_configure_notify_event_t*>(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<xcb_client_message_event_t*>(windowEvent)->window))
return false;
xcb_client_message_event_t* e = reinterpret_cast<xcb_client_message_event_t*>(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<xcb_key_press_event_t*>(windowEvent)->event))
return false;
xcb_key_press_event_t* e = reinterpret_cast<xcb_key_press_event_t*>(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<xcb_key_release_event_t*>(windowEvent)->event))
return false;
xcb_key_release_event_t* e = reinterpret_cast<xcb_key_release_event_t*>(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<xcb_button_press_event_t*>(windowEvent)->event))
return false;
xcb_button_press_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(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<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_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<xcb_motion_notify_event_t*>(windowEvent)->event))
return false;
xcb_motion_notify_event_t* e = reinterpret_cast<xcb_motion_notify_event_t*>(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<xcb_enter_notify_event_t*>(windowEvent)->event))
return false;
xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast<xcb_enter_notify_event_t*>(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<xcb_leave_notify_event_t*>(windowEvent)->event))
return false;
xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast<xcb_leave_notify_event_t*>(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<xcb_reparent_notify_event_t*>(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<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)
{

View File

@ -33,7 +33,7 @@
#include <SFML/System/String.hpp>
#include <X11/Xlib-xcb.h>
#include <xcb/xcb_ewmh.h>
#include <set>
#include <deque>
namespace sf
@ -246,6 +246,17 @@ private:
////////////////////////////////////////////////////////////
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,13 +270,14 @@ private:
////////////////////////////////////////////////////////////
// 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
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<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
Atom m_atomWmProtocols; ///< Atom used to identify WM protocol messages
Atom m_atomClose; ///< Atom used to identify the close event