From 39cdebfe714299af7143e0140e1912ffa84e5fa5 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Fri, 27 Mar 2015 16:53:24 +0100 Subject: [PATCH] Add ScopedXcbPtr to CMakeLists.txt, replaced xcb_query_extension with xcb_get_extension_data where possible, removed decorations from fullscreen windows, fixed DRI2 events not being forwarded as Xlib events leading to Mesa not functioning correctly in certain situations. --- src/SFML/Window/CMakeLists.txt | 2 + src/SFML/Window/Unix/VideoModeImpl.cpp | 27 +-- src/SFML/Window/Unix/WindowImplX11.cpp | 268 +++++++++++++++++++++---- 3 files changed, 234 insertions(+), 63 deletions(-) diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index 0e13ff7bb..d5ab805ee 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -75,6 +75,8 @@ elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD) ${SRCROOT}/Unix/Display.hpp ${SRCROOT}/Unix/InputImpl.cpp ${SRCROOT}/Unix/InputImpl.hpp + ${SRCROOT}/Unix/ScopedXcbPtr.hpp + ${SRCROOT}/Unix/ScopedXcbPtr.inl ${SRCROOT}/Unix/SensorImpl.cpp ${SRCROOT}/Unix/SensorImpl.hpp ${SRCROOT}/Unix/VideoModeImpl.cpp diff --git a/src/SFML/Window/Unix/VideoModeImpl.cpp b/src/SFML/Window/Unix/VideoModeImpl.cpp index 9ac297be3..9ee812f20 100644 --- a/src/SFML/Window/Unix/VideoModeImpl.cpp +++ b/src/SFML/Window/Unix/VideoModeImpl.cpp @@ -50,19 +50,9 @@ std::vector VideoModeImpl::getFullscreenModes() ScopedXcbPtr error(NULL); - // Check if the RandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( - connection, - xcb_query_extension( - connection, - RANDR.size(), - RANDR.c_str() - ), - &error - )); + const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(connection, &xcb_randr_id); - if (error || !randr_ext->present) + if (!randrExt || !randrExt->present) { // Randr extension is not supported: we cannot get the video modes err() << "Failed to use the RandR extension while trying to get the supported video modes" << std::endl; @@ -161,18 +151,9 @@ VideoMode VideoModeImpl::getDesktopMode() ScopedXcbPtr error(NULL); // Check if the RandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( - connection, - xcb_query_extension( - connection, - RANDR.size(), - RANDR.c_str() - ), - &error - )); + const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(connection, &xcb_randr_id); - if (error || !randr_ext->present) + if (!randrExt || !randrExt->present) { // Randr extension is not supported: we cannot get the video modes err() << "Failed to use the RandR extension while trying to get the desktop video mode" << std::endl; diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index b826b70ac..1e1762213 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -36,12 +36,17 @@ #include #include #include +#include #include #include #include #include #include +// So we don't have to require xcb dri2 to be present +#define XCB_DRI2_BUFFER_SWAP_COMPLETE 0 +#define XCB_DRI2_INVALIDATE_BUFFERS 1 + #ifdef SFML_OPENGL_ES #include typedef sf::priv::EglContext ContextType; @@ -58,11 +63,11 @@ 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 | - XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW; + 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 | + XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW; // Find the name of the current executable std::string findExecutableName() @@ -174,6 +179,123 @@ namespace ewmhSupported = true; return true; } + + xcb_query_extension_reply_t getDriExtension() + { + xcb_connection_t* connection = sf::priv::OpenConnection(); + + sf::priv::ScopedXcbPtr error(NULL); + + // Check if the DRI2 extension is present + // We don't use xcb_get_extension_data here to avoid having to link to xcb_dri2 + static const std::string DRI2 = "DRI2"; + sf::priv::ScopedXcbPtr driExt(xcb_query_extension_reply( + connection, + xcb_query_extension( + connection, + DRI2.size(), + DRI2.c_str() + ), + &error + )); + + // Close the connection with the X server + sf::priv::CloseConnection(connection); + + if (error || !driExt || !driExt->present) + { + xcb_query_extension_reply_t reply; + std::memset(&reply, 0, sizeof(reply)); + return reply; + } + + return *driExt.get(); + } + + void dumpXcbExtensions() + { + xcb_connection_t* connection = sf::priv::OpenConnection(); + + sf::priv::ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present + sf::priv::ScopedXcbPtr extensions(xcb_list_extensions_reply( + connection, + xcb_list_extensions( + connection + ), + &error + )); + + if (error || !extensions) + { + // Close the connection with the X server + sf::priv::CloseConnection(connection); + + sf::err() << "Couldn't get list of X extensions" << std::endl; + return; + } + + xcb_str_iterator_t iter = xcb_list_extensions_names_iterator(extensions.get()); + + sf::err() << "X Extensions:" << std::endl; + + while (iter.rem) + { + std::string name(xcb_str_name(iter.data), xcb_str_name_length(iter.data)); + + sf::priv::ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present + sf::priv::ScopedXcbPtr extension(xcb_query_extension_reply( + connection, + xcb_query_extension( + connection, + name.size(), + name.c_str() + ), + &error + )); + + if (error || !extension || !extension->present) + { + sf::err() << "\t" << name << " - Failed to query" << std::endl; + continue; + } + + int firstEvent = extension->first_event; + + sf::err() << "\t" << name << " - First event: " << firstEvent << std::endl; + iter.data += xcb_str_name_length(iter.data) + 1; + --iter.rem; + } + + // Close the connection with the X server + sf::priv::CloseConnection(connection); + } + + void dumpUnhandledEvent(uint8_t type) + { + static std::vector types; + + // Check if we already reported this type + if (std::find(types.begin(), types.end(), type) != types.end()) + return; + + // Insert it if new + types.push_back(type); + + static bool dumpedExtensions = false; + + if (!dumpedExtensions) + { + dumpXcbExtensions(); + dumpedExtensions = true; + } + + sf::err() << "Unhandled event type: " << (static_cast(type) & ~0x80) << std::endl + << "Report this to the SFML maintainers if possible" << std::endl; + } } @@ -350,18 +472,14 @@ m_fullscreen ((style & Style::Fullscreen) != 0) std::memset(&sizeHints, 0, sizeof(sizeHints)); // Set the window's style (tell the window manager to change our window's decorations and functions according to the requested style) - if (!m_fullscreen) - { - // Set the Motif WM hints - setMotifHints(style); + setMotifHints(style); - // This is a hack to force some windows managers to disable resizing - if (!(style & Style::Resize)) - { - m_useSizeHints = true; - xcb_icccm_size_hints_set_min_size(&sizeHints, width, height); - xcb_icccm_size_hints_set_max_size(&sizeHints, width, height); - } + // This is a hack to force some windows managers to disable resizing + if (!(style & Style::Resize)) + { + m_useSizeHints = true; + xcb_icccm_size_hints_set_min_size(&sizeHints, width, height); + xcb_icccm_size_hints_set_max_size(&sizeHints, width, height); } // Set the WM hints of the normal state @@ -1098,18 +1216,9 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) ScopedXcbPtr error(NULL); // Check if the RandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( - m_connection, - xcb_query_extension( - m_connection, - RANDR.size(), - RANDR.c_str() - ), - &error - )); + const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(m_connection, &xcb_randr_id); - if (error || !randr_ext->present) + if (!randrExt || !randrExt->present) { // RandR extension is not supported: we cannot use fullscreen mode err() << "Fullscreen is not supported, switching to window mode" << std::endl; @@ -1445,21 +1554,27 @@ void WindowImplX11::setMotifHints(unsigned long style) hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; hints.decorations = 0; hints.functions = 0; + hints.inputMode = 0; + hints.state = 0; - if (style & Style::Titlebar) + // Decorate only if not in fullscreen + if (!(style & Style::Fullscreen)) { - hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU; - hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE; - } - if (style & Style::Resize) - { - hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH; - hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE; - } - if (style & Style::Close) - { - hints.decorations |= 0; - hints.functions |= MWM_FUNC_CLOSE; + if (style & Style::Titlebar) + { + hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU; + hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE; + } + if (style & Style::Resize) + { + hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH; + hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE; + } + if (style & Style::Close) + { + hints.decorations |= 0; + hints.functions |= MWM_FUNC_CLOSE; + } } ScopedXcbPtr propertyError(xcb_request_check( @@ -2014,6 +2129,79 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) xcb_flush(m_connection); // Discard remaining events break; } + + // The stuff that might pop up but we don't care about + // Whitelist more when necessary + // Window visibility changed (hide/unhide) + case XCB_MAP_NOTIFY: + case XCB_UNMAP_NOTIFY: + { + break; + } + + // Handle the rest + default: + { + uint8_t responseType = windowEvent->response_type & ~0x80; + + // Handle any extension events first + + // DRI2 + static xcb_query_extension_reply_t driExtension = getDriExtension(); + if (driExtension.present) + { + // Because we are using the XCB event queue instead of the Xlib event + // queue, Mesa breaks a bit (VSync among other things) because DRI2 still + // expects certain Xlib events to come its way. We work around this by + // emulating the old Xlib event queue for these specific wire events. + // Sources (retrieved 27 Mar 2015): + // http://wrl.illest.net/post/45342765813/code-tip-glx-and-xcbownseventqueue + // https://bugs.freedesktop.org/show_bug.cgi?id=42131 + // https://bugs.freedesktop.org/show_bug.cgi?id=35945 + // Mesa src/glx/dri2.c + // QtBase src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp + // QtBase Commit bb22b4965070409df4658f16fdf549f0362e8a9c + // Qt Change-Id I3b4ef3f6e3efbae25f49f161e229e9b15e951778 + // QtBase Commit 13c5c81cfa934c9b610720fe79e07465b00ebc8d + // Qt Change-Id Ia93eb8be1cbbc3d8ae7913a934c195af6b5ec538 + // QtBase Commit decb88693c8a7f0c073889b91151f01a850e3adf + // Qt Change-Id Ic466ff26487937b03f072a57e0ee4df335492a5f + if ((responseType == driExtension.first_event + XCB_DRI2_BUFFER_SWAP_COMPLETE) || + (responseType == driExtension.first_event + XCB_DRI2_INVALIDATE_BUFFERS)) + { + // DRI2 BufferSwapComplete and InvalidateBuffers + + // We lock/unlock the display to protect against concurrent access + XLockDisplay(m_display); + + typedef Bool (*wireEventHandler)(Display*, XEvent*, xEvent*); + + // Probe for any handlers that are registered for this event type + wireEventHandler handler = XESetWireToEvent(m_display, responseType, 0); + + if (handler) + { + // Restore the previous handler if one was registered + XESetWireToEvent(m_display, responseType, handler); + + XEvent event; + windowEvent->sequence = LastKnownRequestProcessed(m_display); + + // Pretend to be the Xlib event queue + handler(m_display, &event, reinterpret_cast(windowEvent)); + } + + XUnlockDisplay(m_display); + + return true; + } + } + + // Print any surprises to stderr (would be nice if people report when this happens) + dumpUnhandledEvent(responseType); + + break; + } } return true;