From c08a56bf9f479a83d6d5a57d9fbd5c5565902d2f Mon Sep 17 00:00:00 2001 From: Stefan Schindler Date: Fri, 9 Jan 2015 12:52:59 +0100 Subject: [PATCH 1/8] Adjusted WindowImplX11. * Replaced Xlib event names by XCB equivalents. * Removed XCB_CW_OVERRIDE_REDIRECT in order to let the WM handle mapping the window to the full screen. * Fixed mouse grabbing in fullscreen mode. Removed keyboard grabbing to allow the user to "alt+tab" out of the window. * Completely revised fullscreen handling: The screen's resolution is not changed at all anymore. Instead the WM is asked for going fullscreen and the view is scaled. --- src/SFML/Window/Unix/WindowImplX11.cpp | 257 +++++++++++++------------ src/SFML/Window/Unix/WindowImplX11.hpp | 11 +- 2 files changed, 141 insertions(+), 127 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index d6bb52b8..ea10256d 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -63,9 +62,11 @@ namespace { sf::priv::WindowImplX11* fullscreenWindow = NULL; std::vector allWindows; - unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | - PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | - EnterWindowMask | LeaveWindowMask; + 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() @@ -103,7 +104,8 @@ m_oldVideoMode(-1), m_hiddenCursor(0), m_keyRepeat (true), m_previousSize(-1, -1), -m_useSizeHints(false) +m_useSizeHints(false), +m_fullscreen (false) { // Open a connection with the X server m_display = OpenDisplay(); @@ -150,7 +152,8 @@ m_oldVideoMode(-1), m_hiddenCursor(0), m_keyRepeat (true), m_previousSize(-1, -1), -m_useSizeHints(false) +m_useSizeHints(false), +m_fullscreen ((style & Style::Fullscreen) != 0) { // Open a connection with the X server m_display = OpenDisplay(); @@ -166,23 +169,18 @@ m_useSizeHints(false) XSetEventQueueOwner(m_display, XCBOwnsEventQueue); // Compute position and size - bool fullscreen = (style & Style::Fullscreen) != 0; - int left = fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2; - int top = fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2; + int left = m_fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2; + int top = m_fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2; int width = mode.width; int height = mode.height; - // Switch to fullscreen if necessary - if (fullscreen) - switchToFullscreen(mode); - // Choose the visual according to the context settings XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings); // Define the window attributes xcb_colormap_t colormap = xcb_generate_id(m_connection); xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid); - const uint32_t value_list[] = {fullscreen, static_cast(eventMask), colormap}; + const uint32_t value_list[] = {static_cast(eventMask), colormap}; // Create the window m_window = xcb_generate_id(m_connection); @@ -199,7 +197,7 @@ m_useSizeHints(false) 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualInfo.visualid, - XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP, + XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, value_list ) )); @@ -214,7 +212,7 @@ m_useSizeHints(false) setTitle(title); // Set the window's style (tell the window manager to change our window's decorations and functions according to the requested style) - if (!fullscreen) + if (!m_fullscreen) { static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS"; ScopedXcbPtr hintsAtomReply(xcb_intern_atom_reply( @@ -309,30 +307,9 @@ m_useSizeHints(false) // Do some common initializations initialize(); - // In fullscreen mode, we must grab keyboard and mouse inputs - if (fullscreen) - { - xcb_grab_pointer( - m_connection, - True, - m_window, - 0, - XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC, - m_window, - XCB_NONE, - XCB_CURRENT_TIME - ); - - xcb_grab_keyboard( - m_connection, - True, - m_window, - XCB_CURRENT_TIME, - XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC - ); - } + // Switch to fullscreen. + if (m_fullscreen) + switchToFullscreen(); } @@ -376,6 +353,44 @@ WindowHandle WindowImplX11::getSystemHandle() const } +//////////////////////////////////////////////////////////// +void WindowImplX11::setPointerGrabbed(bool grabbed) +{ + // NOTE: This only works when the window is mapped and visible! + + if (grabbed) + { + ScopedXcbPtr grabReply(xcb_grab_pointer_reply( + m_connection, + xcb_grab_pointer( + m_connection, + 1, + m_screen->root, + XCB_NONE, + XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC, + m_window, + XCB_NONE, + XCB_CURRENT_TIME + ), + NULL + )); + + if (grabReply && grabReply->status != 0) + sf::err() << "Grabbing the pointer failed." << std::endl + << " Status: " + << static_cast(grabReply->status) << std::endl; + else if (!grabReply) + sf::err() << "Grabbing the pointer failed." << std::endl; + } + else + { + xcb_ungrab_pointer(m_connection, XCB_CURRENT_TIME); + xcb_flush(m_connection); + } +} + + //////////////////////////////////////////////////////////// void WindowImplX11::processEvents() { @@ -736,77 +751,85 @@ bool WindowImplX11::hasFocus() const //////////////////////////////////////////////////////////// -void WindowImplX11::switchToFullscreen(const VideoMode& mode) +void WindowImplX11::switchToFullscreen() { - // Check if the XRandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( + // Create atom for _NET_WM_STATE. + static const std::string netWmState = "_NET_WM_STATE"; + ScopedXcbPtr stateReply(xcb_intern_atom_reply( m_connection, - xcb_query_extension( + xcb_intern_atom( m_connection, - RANDR.size(), - RANDR.c_str() - ), - NULL + 0, + netWmState.size(), + netWmState.c_str() + ), + 0 )); - if (randr_ext->present) - { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + // Create atom for _NET_WM_STATE_FULLSCREEN. + static const std::string netWmStateFullscreen = "_NET_WM_STATE_FULLSCREEN"; + ScopedXcbPtr fullscreenReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( m_connection, - xcb_randr_get_screen_info( - m_connection, - m_screen->root - ), - &error - )); + 0, + netWmStateFullscreen.size(), + netWmStateFullscreen.c_str() + ), + 0 + )); - if (!error) - { - // Save the current video mode before we switch to fullscreen - m_oldVideoMode = config->sizeID; + // Set fullscreen property. + ScopedXcbPtr fullscreenError(xcb_request_check( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + stateReply->atom, + XCB_ATOM_ATOM, + 32, + 1, + &fullscreenReply->atom + ) + )); - // Get the available screen sizes - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (config->nSizes > 0)) - { - // Search a matching size - for (int i = 0; i < config->nSizes; ++i) - { - if ((sizes[i].width == static_cast(mode.width)) && - (sizes[i].height == static_cast(mode.height))) - { - // Switch to fullscreen mode - xcb_randr_set_screen_config( - m_connection, - m_screen->root, - config->timestamp, - config->config_timestamp, - i, - config->rotation, - config->rate - ); - - // Set "this" as the current fullscreen window - fullscreenWindow = this; - break; - } - } - } - } - else - { - // Failed to get the screen configuration - err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; - } - } - else + if (fullscreenError) { - // XRandR extension is not supported: we cannot use fullscreen mode - err() << "Fullscreen is not supported, switching to window mode" << std::endl; + sf::err() << "Setting fullscreen failed." << std::endl; + return; } + + // Create atom for _NET_WM_BYPASS_COMPOSITOR. + static const std::string netWmStateBypassCompositor = "_NET_WM_BYPASS_COMPOSITOR"; + ScopedXcbPtr compReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + netWmStateBypassCompositor.size(), + netWmStateBypassCompositor.c_str() + ), + 0 + )); + + // Disable compositor. + ScopedXcbPtr compositorError(xcb_request_check( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + stateReply->atom, + XCB_ATOM_ATOM, + 32, + 1, + &compReply->atom + ) + )); + + if (compositorError) + sf::err() << "xcb_change_property failed, setting fullscreen not possible." << std::endl; } @@ -926,30 +949,6 @@ void WindowImplX11::cleanup() // Restore the previous video mode (in case we were running in fullscreen) if (fullscreenWindow == this) { - // Get current screen info - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( - m_connection, - xcb_randr_get_screen_info( - m_connection, - m_screen->root - ), - &error - )); - - if (!error) - { - // Reset the video mode - xcb_randr_set_screen_config( - m_connection, - m_screen->root, - CurrentTime, - config->config_timestamp, - m_oldVideoMode, - config->rotation, - config->rate - ); - } // Reset the fullscreen window fullscreenWindow = NULL; @@ -1001,12 +1000,20 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) hints.flags &= ~XUrgencyHint; xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); + // Grab pointer if necessary. + if (m_fullscreen) + setPointerGrabbed(true); + break; } // Lost focus event case XCB_FOCUS_OUT: { + // Ungrab pointer if necessary. + if (m_fullscreen) + setPointerGrabbed(false); + // Update the input context if (m_inputContext) XUnsetICFocus(m_inputContext); diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 1b7527a5..2960ee07 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -182,10 +182,16 @@ private: //////////////////////////////////////////////////////////// /// \brief Switch to fullscreen mode /// - /// \param Mode video mode to switch to + //////////////////////////////////////////////////////////// + void switchToFullscreen(); + + //////////////////////////////////////////////////////////// + /// \brief Grab or ungrab mouse pointer. + /// + /// \param grabbed True to grab, false to ungrab. /// //////////////////////////////////////////////////////////// - void switchToFullscreen(const VideoMode& mode); + void setPointerGrabbed(bool grabbed); //////////////////////////////////////////////////////////// /// \brief Do some common initializations after the window has been created @@ -241,6 +247,7 @@ private: 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 From 3faf2a93d635eb982417836d85f910efddd335fa Mon Sep 17 00:00:00 2001 From: binary1248 Date: Thu, 12 Mar 2015 20:27:01 +0100 Subject: [PATCH 2/8] Reverted back to hard switching the fullscreen video mode when necessary on Unix systems, added support for automatically choosing between EWMH fullscreen setting when supported and manual fullscreen setting as the fallback, added support for window responsiveness checks. --- src/SFML/Window/Unix/WindowImplX11.cpp | 678 ++++++++++++++++++++----- src/SFML/Window/Unix/WindowImplX11.hpp | 44 +- 2 files changed, 572 insertions(+), 150 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index ea10256d..568f0d2e 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,104 @@ namespace //Default fallback name return "sfml"; } + + // Check if Extended Window Manager Hints are supported + bool ewmhSupported() + { + xcb_connection_t* connection = sf::priv::OpenConnection(); + + static const std::string NET_SUPPORTING_WM_CHECK = "_NET_SUPPORTING_WM_CHECK"; + sf::priv::ScopedXcbPtr supportingWmAtomReply(xcb_intern_atom_reply( + connection, + xcb_intern_atom( + connection, + 0, + NET_SUPPORTING_WM_CHECK.size(), + NET_SUPPORTING_WM_CHECK.c_str() + ), + NULL + )); + + if (!supportingWmAtomReply) + { + sf::priv::CloseConnection(connection); + return false; + } + + static const std::string NET_SUPPORTED = "_NET_SUPPORTED"; + sf::priv::ScopedXcbPtr supportedAtomReply(xcb_intern_atom_reply( + connection, + xcb_intern_atom( + connection, + 0, + NET_SUPPORTED.size(), + NET_SUPPORTED.c_str() + ), + NULL + )); + + if (!supportedAtomReply) + { + sf::priv::CloseConnection(connection); + return false; + } + + sf::priv::ScopedXcbPtr error(NULL); + + sf::priv::ScopedXcbPtr rootSupportingWindow(xcb_get_property_reply( + connection, + xcb_get_property( + connection, + 0, + sf::priv::XCBDefaultRootWindow(connection), + supportingWmAtomReply->atom, + XCB_ATOM_WINDOW, + 0, + 0x7fffffff + ), + &error + )); + + if (!rootSupportingWindow) + { + sf::priv::CloseConnection(connection); + return false; + } + + xcb_window_t rootWindow = *reinterpret_cast(xcb_get_property_value(rootSupportingWindow.get())); + + sf::priv::ScopedXcbPtr childSupportingWindow(xcb_get_property_reply( + connection, + xcb_get_property( + connection, + 0, + rootWindow, + supportingWmAtomReply->atom, + XCB_ATOM_WINDOW, + 0, + 0x7fffffff + ), + &error + )); + + if (!childSupportingWindow) + { + sf::priv::CloseConnection(connection); + return false; + } + + xcb_window_t childWindow = *reinterpret_cast(xcb_get_property_value(childSupportingWindow.get())); + + // Conforming window managers should return the same window for both queries + if (rootSupportingWindow != childSupportingWindow) + { + sf::priv::CloseConnection(connection); + return false; + } + + sf::priv::CloseConnection(connection); + return true; + } } @@ -95,17 +194,19 @@ namespace priv { //////////////////////////////////////////////////////////// WindowImplX11::WindowImplX11(WindowHandle handle) : -m_window (0), -m_inputMethod (NULL), -m_inputContext(NULL), -m_isExternal (true), -m_atomClose (0), -m_oldVideoMode(-1), -m_hiddenCursor(0), -m_keyRepeat (true), -m_previousSize(-1, -1), -m_useSizeHints(false), -m_fullscreen (false) +m_window (0), +m_inputMethod (NULL), +m_inputContext (NULL), +m_isExternal (true), +m_atomWmProtocols(0), +m_atomClose (0), +m_atomPing (0), +m_oldVideoMode (-1), +m_hiddenCursor (0), +m_keyRepeat (true), +m_previousSize (-1, -1), +m_useSizeHints (false), +m_fullscreen (false) { // Open a connection with the X server m_display = OpenDisplay(); @@ -143,17 +244,19 @@ m_fullscreen (false) //////////////////////////////////////////////////////////// WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) : -m_window (0), -m_inputMethod (NULL), -m_inputContext(NULL), -m_isExternal (false), -m_atomClose (0), -m_oldVideoMode(-1), -m_hiddenCursor(0), -m_keyRepeat (true), -m_previousSize(-1, -1), -m_useSizeHints(false), -m_fullscreen ((style & Style::Fullscreen) != 0) +m_window (0), +m_inputMethod (NULL), +m_inputContext (NULL), +m_isExternal (false), +m_atomWmProtocols(0), +m_atomClose (0), +m_atomPing (0), +m_oldVideoMode (-1), +m_hiddenCursor (0), +m_keyRepeat (true), +m_previousSize (-1, -1), +m_useSizeHints (false), +m_fullscreen ((style & Style::Fullscreen) != 0) { // Open a connection with the X server m_display = OpenDisplay(); @@ -174,13 +277,17 @@ m_fullscreen ((style & Style::Fullscreen) != 0) int width = mode.width; int height = mode.height; + // Set fullscreen video mode if necessary + if (m_fullscreen) + setVideoMode(mode); + // Choose the visual according to the context settings XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings); // Define the window attributes xcb_colormap_t colormap = xcb_generate_id(m_connection); xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid); - const uint32_t value_list[] = {static_cast(eventMask), colormap}; + const uint32_t value_list[] = {m_fullscreen && !ewmhSupported(), static_cast(eventMask), colormap}; // Create the window m_window = xcb_generate_id(m_connection); @@ -197,7 +304,7 @@ m_fullscreen ((style & Style::Fullscreen) != 0) 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualInfo.visualid, - XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, + XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP, value_list ) )); @@ -276,16 +383,22 @@ m_fullscreen ((style & Style::Fullscreen) != 0) hints.functions |= MWM_FUNC_CLOSE; } - xcb_change_property( + ScopedXcbPtr propertyError(xcb_request_check( m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - hintsAtomReply->atom, - XCB_ATOM_WM_HINTS, - sizeof(hints.flags) * 8, - sizeof(hints) / sizeof(hints.flags), - reinterpret_cast(&hints) - ); + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + hintsAtomReply->atom, + hintsAtomReply->atom, + sizeof(hints.flags) * 8, + sizeof(hints) / sizeof(hints.flags), + reinterpret_cast(&hints) + ) + )); + + if (propertyError) + err() << "xcb_change_property failed, could not set window hints" << std::endl; } // This is a hack to force some windows managers to disable resizing @@ -307,7 +420,7 @@ m_fullscreen ((style & Style::Fullscreen) != 0) // Do some common initializations initialize(); - // Switch to fullscreen. + // Switch to fullscreen if necessary if (m_fullscreen) switchToFullscreen(); } @@ -377,11 +490,11 @@ void WindowImplX11::setPointerGrabbed(bool grabbed) )); if (grabReply && grabReply->status != 0) - sf::err() << "Grabbing the pointer failed." << std::endl + err() << "Grabbing the pointer failed." << std::endl << " Status: " << static_cast(grabReply->status) << std::endl; else if (!grabReply) - sf::err() << "Grabbing the pointer failed." << std::endl; + err() << "Grabbing the pointer failed." << std::endl; } else { @@ -685,7 +798,7 @@ void WindowImplX11::requestFocus() break; } } - + // Check if window is viewable (not on other desktop, ...) // TODO: Check also if minimized ScopedXcbPtr attributes(xcb_get_window_attributes_reply( @@ -699,12 +812,12 @@ void WindowImplX11::requestFocus() if (!attributes) { - sf::err() << "Failed to check if window is viewable while requesting focus" << std::endl; + err() << "Failed to check if window is viewable while requesting focus" << std::endl; return; // error getting attribute } bool windowViewable = (attributes->map_state == XCB_MAP_STATE_VIEWABLE); - + if (sfmlWindowFocused && windowViewable) { // Another SFML window of this application has the focus and the current window is viewable: @@ -750,93 +863,283 @@ bool WindowImplX11::hasFocus() const } +//////////////////////////////////////////////////////////// +void WindowImplX11::setVideoMode(const VideoMode& mode) +{ + // Skip mode switching if the new mode is equal to the desktop mode + if (mode == VideoMode::getDesktopMode()) + return; + + // Check if the XRandR 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() + ), + NULL + )); + + if (randr_ext->present) + { + // Get the current configuration + ScopedXcbPtr error(NULL); + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + m_connection, + xcb_randr_get_screen_info( + m_connection, + m_screen->root + ), + &error + )); + + if (!error) + { + // Save the current video mode before we switch to fullscreen + m_oldVideoMode = config->sizeID; + + // Get the available screen sizes + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + if (sizes && (config->nSizes > 0)) + { + // Search a matching size + for (int i = 0; i < config->nSizes; ++i) + { + if ((sizes[i].width == static_cast(mode.width)) && + (sizes[i].height == static_cast(mode.height))) + { + // Switch to fullscreen mode + xcb_randr_set_screen_config( + m_connection, + m_screen->root, + config->timestamp, + config->config_timestamp, + i, + config->rotation, + config->rate + ); + + // Set "this" as the current fullscreen window + fullscreenWindow = this; + break; + } + } + } + } + else + { + // Failed to get the screen configuration + err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + } + } + else + { + // XRandR extension is not supported: we cannot use fullscreen mode + err() << "Fullscreen is not supported, switching to window mode" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::resetVideoMode() +{ + if (fullscreenWindow == this) + { + // Get current screen info + ScopedXcbPtr error(NULL); + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + m_connection, + xcb_randr_get_screen_info( + m_connection, + m_screen->root + ), + &error + )); + + if (!error) + { + // Reset the video mode + xcb_randr_set_screen_config( + m_connection, + m_screen->root, + CurrentTime, + config->config_timestamp, + m_oldVideoMode, + config->rotation, + config->rate + ); + } + + // Reset the fullscreen window + fullscreenWindow = NULL; + } +} + + //////////////////////////////////////////////////////////// void WindowImplX11::switchToFullscreen() { - // Create atom for _NET_WM_STATE. - static const std::string netWmState = "_NET_WM_STATE"; - ScopedXcbPtr stateReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - netWmState.size(), - netWmState.c_str() - ), - 0 - )); - - // Create atom for _NET_WM_STATE_FULLSCREEN. - static const std::string netWmStateFullscreen = "_NET_WM_STATE_FULLSCREEN"; - ScopedXcbPtr fullscreenReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - netWmStateFullscreen.size(), - netWmStateFullscreen.c_str() - ), - 0 - )); - - // Set fullscreen property. - ScopedXcbPtr fullscreenError(xcb_request_check( - m_connection, - xcb_change_property_checked( - m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - stateReply->atom, - XCB_ATOM_ATOM, - 32, - 1, - &fullscreenReply->atom - ) - )); - - if (fullscreenError) + // Try EWMH method if supported or fallback to manual + if (ewmhSupported()) { - sf::err() << "Setting fullscreen failed." << std::endl; - return; + // Create atom for _NET_WM_BYPASS_COMPOSITOR. + static const std::string netWmStateBypassCompositor = "_NET_WM_BYPASS_COMPOSITOR"; + ScopedXcbPtr compReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + netWmStateBypassCompositor.size(), + netWmStateBypassCompositor.c_str() + ), + 0 + )); + + static const Uint32 bypassCompositor = 1; + + // Disable compositor. + ScopedXcbPtr compositorError(xcb_request_check( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + compReply->atom, + XCB_ATOM_CARDINAL, + 32, + 1, + &bypassCompositor + ) + )); + + // Not being able to bypass the compositor is not a fatal error + if (compositorError) + err() << "xcb_change_property failed, setting fullscreen not possible" << std::endl; + + // Create atom for _NET_ACTIVE_WINDOW. + static const std::string netActiveWindow = "_NET_ACTIVE_WINDOW"; + ScopedXcbPtr activeWindowReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + netActiveWindow.size(), + netActiveWindow.c_str() + ), + 0 + )); + + if (!activeWindowReply) + { + err() << "Setting fullscreen failed, could not get \"_NET_ACTIVE_WINDOW\" atom" << std::endl; + return; + } + + xcb_client_message_event_t event; + + event.response_type = XCB_CLIENT_MESSAGE; + event.window = m_window; + event.format = 32; + event.sequence = 0; + event.type = activeWindowReply->atom; + event.data.data32[0] = 1; + event.data.data32[1] = XCB_CURRENT_TIME; + + ScopedXcbPtr activeWindowError(xcb_request_check( + m_connection, + xcb_send_event_checked( + m_connection, + 0, + XCBDefaultRootWindow(m_connection), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + reinterpret_cast(&event) + ) + )); + + if (activeWindowError) + { + err() << "Setting fullscreen failed, could not send \"_NET_ACTIVE_WINDOW\" event" << std::endl; + return; + } + + // Create atom for _NET_WM_STATE. + static const std::string netWmState = "_NET_WM_STATE"; + ScopedXcbPtr stateReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + netWmState.size(), + netWmState.c_str() + ), + 0 + )); + + if (!stateReply) + { + err() << "Setting fullscreen failed. Could not get \"_NET_WM_STATE\" atom" << std::endl; + return; + } + + // Create atom for _NET_WM_STATE_FULLSCREEN. + static const std::string netWmStateFullscreen = "_NET_WM_STATE_FULLSCREEN"; + ScopedXcbPtr fullscreenReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + netWmStateFullscreen.size(), + netWmStateFullscreen.c_str() + ), + 0 + )); + + if (!fullscreenReply) + { + err() << "Setting fullscreen failed. Could not get \"_NET_WM_STATE_FULLSCREEN\" atom" << std::endl; + return; + } + + event.response_type = XCB_CLIENT_MESSAGE; + event.window = m_window; + event.format = 32; + event.sequence = 0; + event.type = stateReply->atom; + event.data.data32[0] = 1; // _NET_WM_STATE_ADD + event.data.data32[1] = fullscreenReply->atom; + event.data.data32[2] = 0; + event.data.data32[3] = 1; + + ScopedXcbPtr wmStateError(xcb_request_check( + m_connection, + xcb_send_event_checked( + m_connection, + 0, + XCBDefaultRootWindow(m_connection), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + reinterpret_cast(&event) + ) + )); + + if (wmStateError) + err() << "Setting fullscreen failed. Could not send \"_NET_WM_STATE\" event" << std::endl; + } + else + { + // If WM fullscreen setting is not available, we need to do it manually + const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; + xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); + xcb_set_input_focus(m_connection, XCB_INPUT_FOCUS_POINTER_ROOT, m_window, XCB_CURRENT_TIME); } - - // Create atom for _NET_WM_BYPASS_COMPOSITOR. - static const std::string netWmStateBypassCompositor = "_NET_WM_BYPASS_COMPOSITOR"; - ScopedXcbPtr compReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - netWmStateBypassCompositor.size(), - netWmStateBypassCompositor.c_str() - ), - 0 - )); - - // Disable compositor. - ScopedXcbPtr compositorError(xcb_request_check( - m_connection, - xcb_change_property_checked( - m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - stateReply->atom, - XCB_ATOM_ATOM, - 32, - 1, - &compReply->atom - ) - )); - - if (compositorError) - sf::err() << "xcb_change_property failed, setting fullscreen not possible." << std::endl; } //////////////////////////////////////////////////////////// void WindowImplX11::initialize() { - // Get the atoms for registering the close event + // Get the atom for registering the close event static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; ScopedXcbPtr deleteWindowAtomReply(xcb_intern_atom_reply( m_connection, @@ -849,6 +1152,7 @@ void WindowImplX11::initialize() NULL )); + // Get the atom for setting the window protocols we support static const std::string WM_PROTOCOLS_NAME = "WM_PROTOCOLS"; ScopedXcbPtr protocolsAtomReply(xcb_intern_atom_reply( m_connection, @@ -861,22 +1165,104 @@ void WindowImplX11::initialize() NULL )); - if (protocolsAtomReply && deleteWindowAtomReply) - { - xcb_icccm_set_wm_protocols( + // Get the atom for registering the ping event + static const std::string NET_WM_PING = "_NET_WM_PING"; + sf::priv::ScopedXcbPtr wmPingReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( m_connection, - m_window, - protocolsAtomReply->atom, - 1, - &deleteWindowAtomReply->atom - ); + 0, + NET_WM_PING.size(), + NET_WM_PING.c_str() + ), + NULL + )); - m_atomClose = deleteWindowAtomReply->atom; + // Get the atom for setting the process ID of the window + static const std::string NET_WM_PID = "_NET_WM_PID"; + sf::priv::ScopedXcbPtr wmPidReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + NET_WM_PID.size(), + NET_WM_PID.c_str() + ), + NULL + )); + + if (protocolsAtomReply) + { + m_atomWmProtocols = protocolsAtomReply->atom; + + xcb_atom_t atoms[2]; + Uint32 atomCount = 0; + + if (deleteWindowAtomReply && ewmhSupported() && wmPingReply && wmPidReply) + { + atoms[0] = deleteWindowAtomReply->atom; + atomCount = 1; + + m_atomClose = deleteWindowAtomReply->atom; + + Uint32 pid = getpid(); + + // Set PID. + ScopedXcbPtr pidError(xcb_request_check( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + wmPidReply->atom, + XCB_ATOM_CARDINAL, + 32, + 1, + &pid + ) + )); + + if (!pidError) + { + atoms[1] = wmPingReply->atom; + atomCount = 2; + + m_atomPing = wmPingReply->atom; + } + } + else if (deleteWindowAtomReply) + { + atoms[0] = deleteWindowAtomReply->atom; + atomCount = 1; + + m_atomClose = deleteWindowAtomReply->atom; + } + + if (atomCount) + { + ScopedXcbPtr setProtocolsError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_protocols( + m_connection, + m_window, + protocolsAtomReply->atom, + atomCount, + atoms + ) + )); + + if (setProtocolsError) + err() << "Failed to set window protocols" << std::endl; + } + else + { + err() << "Didn't set any window protocols" << std::endl; + } } else { // Should not happen, but better safe than sorry. - std::cerr << "Failed to request WM_PROTOCOLS/WM_DELETE_WINDOW_NAME atoms." << std::endl; + err() << "Failed to request WM_PROTOCOLS atoms." << std::endl; } // Create the input context @@ -888,7 +1274,7 @@ void WindowImplX11::initialize() m_inputMethod, XNClientWindow, m_window, - XNFocusWindow, + XNFocusWindow, m_window, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, @@ -947,12 +1333,7 @@ void WindowImplX11::createHiddenCursor() void WindowImplX11::cleanup() { // Restore the previous video mode (in case we were running in fullscreen) - if (fullscreenWindow == this) - { - - // Reset the fullscreen window - fullscreenWindow = NULL; - } + resetVideoMode(); // Unhide the mouse cursor (in case it was hidden) setMouseCursorVisible(true); @@ -1040,11 +1421,36 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) case XCB_CLIENT_MESSAGE: { xcb_client_message_event_t* e = reinterpret_cast(windowEvent); - if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomClose)) + + // Handle window manager protocol messages we support + if (e->type == m_atomWmProtocols) { - Event event; - event.type = Event::Closed; - pushEvent(event); + if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomClose)) + { + // Handle the WM_DELETE_WINDOW message + Event event; + event.type = Event::Closed; + pushEvent(event); + } + else if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomPing)) + { + // Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive + e->window = XCBDefaultRootWindow(m_connection); + + ScopedXcbPtr pongError(xcb_request_check( + m_connection, + xcb_send_event_checked( + m_connection, + 0, + XCBDefaultRootWindow(m_connection), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + reinterpret_cast(e) + ) + )); + + if (pongError) + err() << "Could not send pong event back to WM" << std::endl; + } } break; } diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 2960ee07..b5aa1900 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -179,6 +179,20 @@ protected: private: + //////////////////////////////////////////////////////////// + /// \brief Set fullscreen video mode + /// + /// \param Mode video mode to switch to + /// + //////////////////////////////////////////////////////////// + void setVideoMode(const VideoMode& mode); + + //////////////////////////////////////////////////////////// + /// \brief Reset to desktop video mode + /// + //////////////////////////////////////////////////////////// + void resetVideoMode(); + //////////////////////////////////////////////////////////// /// \brief Switch to fullscreen mode /// @@ -234,20 +248,22 @@ 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_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_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? + ::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_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 + Atom m_atomPing; ///< Atom used to identify the ping 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 From 623f63a48aaff72062ac7c4adc06f96506ce65c9 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Sat, 14 Mar 2015 06:17:41 +0100 Subject: [PATCH 3/8] Fixed RandR extension not being loaded causing Unix screen mode switching to fail, added several more error checks to RandR operations, added support for rotated resolutions on Unix (#771). --- src/SFML/Window/Unix/VideoModeImpl.cpp | 212 +++++++++++++------- src/SFML/Window/Unix/WindowImplX11.cpp | 255 ++++++++++++++++--------- 2 files changed, 303 insertions(+), 164 deletions(-) diff --git a/src/SFML/Window/Unix/VideoModeImpl.cpp b/src/SFML/Window/Unix/VideoModeImpl.cpp index cef67bcd..9ac297be 100644 --- a/src/SFML/Window/Unix/VideoModeImpl.cpp +++ b/src/SFML/Window/Unix/VideoModeImpl.cpp @@ -48,7 +48,9 @@ std::vector VideoModeImpl::getFullscreenModes() // Retrieve the default screen xcb_screen_t* screen = XCBDefaultScreen(connection); - // Check if the XRandR extension is present + ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present static const std::string RANDR = "RANDR"; ScopedXcbPtr randr_ext(xcb_query_extension_reply( connection, @@ -56,56 +58,86 @@ std::vector VideoModeImpl::getFullscreenModes() connection, RANDR.size(), RANDR.c_str() - ), - NULL + ), + &error )); - if (randr_ext->present) + if (error || !randr_ext->present) { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + // 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; + + // Close the connection with the X server + CloseConnection(connection); + + return modes; + } + + // Load RandR and check its version + ScopedXcbPtr randrVersion(xcb_randr_query_version_reply( + connection, + xcb_randr_query_version( connection, - xcb_randr_get_screen_info( - connection, - screen->root - ), - &error - )); + 1, + 1 + ), + &error + )); - if (!error) + if (error) + { + err() << "Failed to load the RandR extension while trying to get the supported video modes" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return modes; + } + + // Get the current configuration + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + connection, + xcb_randr_get_screen_info( + connection, + screen->root + ), + &error + )); + + if (error) + { + // Failed to get the screen configuration + err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return modes; + } + + // Get the available screen sizes + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + if (sizes && (config->nSizes > 0)) + { + // Get the list of supported depths + xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); + // Combine depths and sizes to fill the array of supported modes + for (; iter.rem; xcb_depth_next(&iter)) { - // Get the available screen sizes - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (config->nSizes > 0)) + for (int j = 0; j < config->nSizes; ++j) { - // Get the list of supported depths - xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); - // Combine depths and sizes to fill the array of supported modes - for (; iter.rem; xcb_depth_next(&iter)) - { - for (int j = 0; j < config->nSizes; ++j) - { - // Convert to VideoMode - VideoMode mode(sizes[j].width, sizes[j].height, iter.data->depth); + // Convert to VideoMode + VideoMode mode(sizes[j].width, sizes[j].height, iter.data->depth); - // Add it only if it is not already in the array - if (std::find(modes.begin(), modes.end(), mode) == modes.end()) - modes.push_back(mode); - } - } + if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 || + config->rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(mode.width, mode.height); + + // Add it only if it is not already in the array + if (std::find(modes.begin(), modes.end(), mode) == modes.end()) + modes.push_back(mode); } } - else - { - // Failed to get the screen configuration - err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl; - } - } - else - { - // XRandr extension is not supported: we cannot get the video modes - err() << "Failed to use the XRandR extension while trying to get the supported video modes" << std::endl; } // Close the connection with the X server @@ -126,7 +158,9 @@ VideoMode VideoModeImpl::getDesktopMode() // Retrieve the default screen xcb_screen_t* screen = XCBDefaultScreen(connection); - // Check if the XRandR extension is present + ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present static const std::string RANDR = "RANDR"; ScopedXcbPtr randr_ext(xcb_query_extension_reply( connection, @@ -134,44 +168,80 @@ VideoMode VideoModeImpl::getDesktopMode() connection, RANDR.size(), RANDR.c_str() - ), - NULL + ), + &error )); - if (randr_ext->present) + if (error || !randr_ext->present) { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + // 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; + + // Close the connection with the X server + CloseConnection(connection); + + return desktopMode; + } + + // Load RandR and check its version + ScopedXcbPtr randrVersion(xcb_randr_query_version_reply( + connection, + xcb_randr_query_version( connection, - xcb_randr_get_screen_info( - connection, - screen->root - ), - &error - )); + 1, + 1 + ), + &error + )); - if (!error) - { - // Get the current video mode - xcb_randr_mode_t currentMode = config->sizeID; + if (error) + { + err() << "Failed to load the RandR extension while trying to get the desktop video mode" << std::endl; - // Get the available screen sizes - int nbSizes = xcb_randr_get_screen_info_sizes_length(config.get()); - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (nbSizes > 0)) - desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth); - } - else - { - // Failed to get the screen configuration - err() << "Failed to retrieve the screen configuration while trying to get the desktop video modes" << std::endl; - } + // Close the connection with the X server + CloseConnection(connection); + + return desktopMode; + } + + // Get the current configuration + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + connection, + xcb_randr_get_screen_info( + connection, + screen->root + ), + &error + )); + + if (error) + { + // Failed to get the screen configuration + err() << "Failed to retrieve the screen configuration while trying to get the desktop video mode" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return desktopMode; + } + + // Get the current video mode + xcb_randr_mode_t currentMode = config->sizeID; + + // Get the available screen sizes + int nbSizes = xcb_randr_get_screen_info_sizes_length(config.get()); + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + if (sizes && (nbSizes > 0)) + { + desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth); + + if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 || + config->rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(desktopMode.width, desktopMode.height); } else { - // XRandr extension is not supported: we cannot get the video modes - err() << "Failed to use the XRandR extension while trying to get the desktop video modes" << std::endl; + err() << "Failed to retrieve any screen sizes while trying to get the desktop video mode" << std::endl; } // Close the connection with the X server diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 568f0d2e..e69e24a8 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -91,6 +91,14 @@ namespace // Check if Extended Window Manager Hints are supported bool ewmhSupported() { + static bool checked = false; + static bool ewmhSupported = false; + + if (checked) + return ewmhSupported; + + checked = true; + xcb_connection_t* connection = sf::priv::OpenConnection(); static const std::string NET_SUPPORTING_WM_CHECK = "_NET_SUPPORTING_WM_CHECK"; @@ -98,7 +106,7 @@ namespace connection, xcb_intern_atom( connection, - 0, + 1, NET_SUPPORTING_WM_CHECK.size(), NET_SUPPORTING_WM_CHECK.c_str() ), @@ -116,7 +124,7 @@ namespace connection, xcb_intern_atom( connection, - 0, + 1, NET_SUPPORTED.size(), NET_SUPPORTED.c_str() ), @@ -145,7 +153,10 @@ namespace &error )); - if (!rootSupportingWindow) + if (!rootSupportingWindow || + (rootSupportingWindow->length != 1) || + (rootSupportingWindow->format != 32) || + (rootSupportingWindow->type != XCB_ATOM_WINDOW)) { sf::priv::CloseConnection(connection); return false; @@ -167,7 +178,10 @@ namespace &error )); - if (!childSupportingWindow) + if (!childSupportingWindow || + (childSupportingWindow->length != 1) || + (childSupportingWindow->format != 32) || + (childSupportingWindow->type != XCB_ATOM_WINDOW)) { sf::priv::CloseConnection(connection); return false; @@ -183,6 +197,7 @@ namespace } sf::priv::CloseConnection(connection); + ewmhSupported = true; return true; } } @@ -218,6 +233,9 @@ m_fullscreen (false) return; } + // Make sure to check for EWMH support before we do anything + ewmhSupported(); + m_screen = XCBDefaultScreen(m_connection); XSetEventQueueOwner(m_display, XCBOwnsEventQueue); @@ -268,6 +286,9 @@ m_fullscreen ((style & Style::Fullscreen) != 0) return; } + // Make sure to check for EWMH support before we do anything + ewmhSupported(); + m_screen = XCBDefaultScreen(m_connection); XSetEventQueueOwner(m_display, XCBOwnsEventQueue); @@ -277,10 +298,6 @@ m_fullscreen ((style & Style::Fullscreen) != 0) int width = mode.width; int height = mode.height; - // Set fullscreen video mode if necessary - if (m_fullscreen) - setVideoMode(mode); - // Choose the visual according to the context settings XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings); @@ -420,9 +437,13 @@ m_fullscreen ((style & Style::Fullscreen) != 0) // Do some common initializations initialize(); - // Switch to fullscreen if necessary + // Set fullscreen video mode and switch to fullscreen if necessary if (m_fullscreen) + { + setPosition(Vector2i(0, 0)); + setVideoMode(mode); switchToFullscreen(); + } } @@ -870,7 +891,9 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) if (mode == VideoMode::getDesktopMode()) return; - // Check if the XRandR extension is present + 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, @@ -879,66 +902,97 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) RANDR.size(), RANDR.c_str() ), - NULL + &error )); - if (randr_ext->present) + if (error || !randr_ext->present) { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( - m_connection, - xcb_randr_get_screen_info( - m_connection, - m_screen->root - ), - &error - )); - - if (!error) - { - // Save the current video mode before we switch to fullscreen - m_oldVideoMode = config->sizeID; - - // Get the available screen sizes - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (config->nSizes > 0)) - { - // Search a matching size - for (int i = 0; i < config->nSizes; ++i) - { - if ((sizes[i].width == static_cast(mode.width)) && - (sizes[i].height == static_cast(mode.height))) - { - // Switch to fullscreen mode - xcb_randr_set_screen_config( - m_connection, - m_screen->root, - config->timestamp, - config->config_timestamp, - i, - config->rotation, - config->rate - ); - - // Set "this" as the current fullscreen window - fullscreenWindow = this; - break; - } - } - } - } - else - { - // Failed to get the screen configuration - err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; - } - } - else - { - // XRandR extension is not supported: we cannot use fullscreen mode + // RandR extension is not supported: we cannot use fullscreen mode err() << "Fullscreen is not supported, switching to window mode" << std::endl; + return; } + + // Load RandR and check its version + ScopedXcbPtr randrVersion(xcb_randr_query_version_reply( + m_connection, + xcb_randr_query_version( + m_connection, + 1, + 1 + ), + &error + )); + + if (error) + { + err() << "Failed to load RandR, switching to window mode" << std::endl; + return; + } + + // Get the current configuration + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + m_connection, + xcb_randr_get_screen_info( + m_connection, + m_screen->root + ), + &error + )); + + if (error) + { + // Failed to get the screen configuration + err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Save the current video mode before we switch to fullscreen + m_oldVideoMode = config->sizeID; + + // Get the available screen sizes + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + + if (!sizes || !config->nSizes) + { + err() << "Failed to get the fullscreen sizes, switching to window mode" << std::endl; + return; + } + + // Search for a matching size + for (int i = 0; i < config->nSizes; ++i) + { + if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 || + config->rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(sizes[i].height, sizes[i].width); + + if ((sizes[i].width == static_cast(mode.width)) && + (sizes[i].height == static_cast(mode.height))) + { + // Switch to fullscreen mode + ScopedXcbPtr setScreenConfig(xcb_randr_set_screen_config_reply( + m_connection, + xcb_randr_set_screen_config( + m_connection, + m_screen->root, + XCB_CURRENT_TIME, + config->config_timestamp, + i, + config->rotation, + 0//config->rate + ), + &error + )); + + if (error) + err() << "Failed to set new screen configuration" << std::endl; + + // Set "this" as the current fullscreen window + fullscreenWindow = this; + return; + } + } + + err() << "Failed to find matching fullscreen size, switching to window mode" << std::endl; } @@ -961,15 +1015,22 @@ void WindowImplX11::resetVideoMode() if (!error) { // Reset the video mode - xcb_randr_set_screen_config( + ScopedXcbPtr setScreenConfig(xcb_randr_set_screen_config_reply( m_connection, - m_screen->root, - CurrentTime, - config->config_timestamp, - m_oldVideoMode, - config->rotation, - config->rate - ); + xcb_randr_set_screen_config( + m_connection, + m_screen->root, + CurrentTime, + config->config_timestamp, + m_oldVideoMode, + config->rotation, + config->rate + ), + &error + )); + + if (error) + err() << "Failed to reset old screen configuration" << std::endl; } // Reset the fullscreen window @@ -1016,7 +1077,7 @@ void WindowImplX11::switchToFullscreen() // Not being able to bypass the compositor is not a fatal error if (compositorError) - err() << "xcb_change_property failed, setting fullscreen not possible" << std::endl; + err() << "xcb_change_property failed, unable to set _NET_WM_BYPASS_COMPOSITOR" << std::endl; // Create atom for _NET_ACTIVE_WINDOW. static const std::string netActiveWindow = "_NET_ACTIVE_WINDOW"; @@ -1037,15 +1098,15 @@ void WindowImplX11::switchToFullscreen() return; } - xcb_client_message_event_t event; + xcb_client_message_event_t activeWindowEvent; + std::memset(&activeWindowEvent, 0, sizeof(activeWindowEvent)); - event.response_type = XCB_CLIENT_MESSAGE; - event.window = m_window; - event.format = 32; - event.sequence = 0; - event.type = activeWindowReply->atom; - event.data.data32[0] = 1; - event.data.data32[1] = XCB_CURRENT_TIME; + activeWindowEvent.response_type = XCB_CLIENT_MESSAGE; + activeWindowEvent.window = m_window; + activeWindowEvent.format = 32; + activeWindowEvent.type = activeWindowReply->atom; + activeWindowEvent.data.data32[0] = 1; + activeWindowEvent.data.data32[1] = XCB_CURRENT_TIME; ScopedXcbPtr activeWindowError(xcb_request_check( m_connection, @@ -1054,7 +1115,7 @@ void WindowImplX11::switchToFullscreen() 0, XCBDefaultRootWindow(m_connection), XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - reinterpret_cast(&event) + reinterpret_cast(&activeWindowEvent) ) )); @@ -1102,15 +1163,17 @@ void WindowImplX11::switchToFullscreen() return; } - event.response_type = XCB_CLIENT_MESSAGE; - event.window = m_window; - event.format = 32; - event.sequence = 0; - event.type = stateReply->atom; - event.data.data32[0] = 1; // _NET_WM_STATE_ADD - event.data.data32[1] = fullscreenReply->atom; - event.data.data32[2] = 0; - event.data.data32[3] = 1; + xcb_client_message_event_t wmStateEvent; + std::memset(&wmStateEvent, 0, sizeof(wmStateEvent)); + + wmStateEvent.response_type = XCB_CLIENT_MESSAGE; + wmStateEvent.window = m_window; + wmStateEvent.format = 32; + wmStateEvent.type = stateReply->atom; + wmStateEvent.data.data32[0] = 1; // _NET_WM_STATE_ADD + wmStateEvent.data.data32[1] = fullscreenReply->atom; + wmStateEvent.data.data32[2] = 0; + wmStateEvent.data.data32[3] = 1; ScopedXcbPtr wmStateError(xcb_request_check( m_connection, @@ -1119,7 +1182,7 @@ void WindowImplX11::switchToFullscreen() 0, XCBDefaultRootWindow(m_connection), XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - reinterpret_cast(&event) + reinterpret_cast(&wmStateEvent) ) )); @@ -1291,6 +1354,12 @@ void WindowImplX11::initialize() // Show the window xcb_map_window(m_connection, m_window); + + // Raise the window and grab input focus + xcb_set_input_focus(m_connection, XCB_INPUT_FOCUS_POINTER_ROOT, m_window, XCB_CURRENT_TIME); + const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; + xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); + xcb_flush(m_connection); // Create the hidden cursor From c22987731384a7be75bf189a017cba0e4ee16610 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Wed, 25 Mar 2015 11:29:51 +0100 Subject: [PATCH 4/8] Fixed numerous bugs/undefined behavior in the XCB implementation, added a lot more XCB error handling and reporting, make use of xcb-ewmh to handle EWMH for us, refactored some code out of the Window constructor into their own methods, fixed fullscreen state transition not working on window managers that create temporary parent windows when the window is being mapped, removed automatic fullscreen pointer grabbing since that is the subject of #614 and might not be desired in some situations. --- src/SFML/Window/CMakeLists.txt | 2 +- src/SFML/Window/Unix/InputImpl.cpp | 113 ++- src/SFML/Window/Unix/WindowImplX11.cpp | 1210 ++++++++++++++---------- src/SFML/Window/Unix/WindowImplX11.hpp | 51 +- 4 files changed, 830 insertions(+), 546 deletions(-) diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index b420fad1..0e13ff7b 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -197,7 +197,7 @@ if(NOT SFML_OPENGL_ES) find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) if(SFML_OS_LINUX OR SFML_OS_FREEBSD) - find_package(XCB COMPONENTS xlib_xcb icccm image randr util REQUIRED) + find_package(XCB COMPONENTS xlib_xcb icccm image randr util ewmh REQUIRED) if(NOT LIBXCB_FOUND) message(FATAL_ERROR "Xcb library not found") endif() diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index ecdf47fa..ee4e2311 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -157,12 +158,27 @@ bool InputImpl::isKeyPressed(Keyboard::Key key) KeyCode keycode = XKeysymToKeycode(display, keysym); if (keycode != 0) { + ScopedXcbPtr error(NULL); + // Get the whole keyboard state - ScopedXcbPtr keymap(xcb_query_keymap_reply(connection, xcb_query_keymap(connection), NULL)); + ScopedXcbPtr keymap( + xcb_query_keymap_reply( + connection, + xcb_query_keymap(connection), + &error + ) + ); // Close the connection with the X server CloseDisplay(display); + if (error) + { + err() << "Failed to query keymap" << std::endl; + + return false; + } + // Check our keycode return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; } @@ -189,13 +205,32 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); + ScopedXcbPtr error(NULL); + // Get pointer mask - ScopedXcbPtr pointer(xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XCBDefaultRootWindow(connection)), NULL)); - uint16_t buttons = pointer->mask; + ScopedXcbPtr pointer( + xcb_query_pointer_reply( + connection, + xcb_query_pointer( + connection, + XCBDefaultRootWindow(connection) + ), + &error + ) + ); // Close the connection with the X server CloseConnection(connection); + if (error) + { + err() << "Failed to query pointer" << std::endl; + + return false; + } + + uint16_t buttons = pointer->mask; + switch (button) { case Mouse::Left: return buttons & XCB_BUTTON_MASK_1; @@ -214,11 +249,29 @@ Vector2i InputImpl::getMousePosition() // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - ScopedXcbPtr pointer(xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XCBDefaultRootWindow(connection)), NULL)); + ScopedXcbPtr error(NULL); + + ScopedXcbPtr pointer( + xcb_query_pointer_reply( + connection, + xcb_query_pointer( + connection, + XCBDefaultRootWindow(connection) + ), + &error + ) + ); // Close the connection with the X server CloseConnection(connection); + if (error) + { + err() << "Failed to query pointer" << std::endl; + + return Vector2i(0, 0); + } + return Vector2i(pointer->root_x, pointer->root_y); } @@ -232,11 +285,29 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - ScopedXcbPtr pointer(xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL)); + ScopedXcbPtr error(NULL); + + ScopedXcbPtr pointer( + xcb_query_pointer_reply( + connection, + xcb_query_pointer( + connection, + XCBDefaultRootWindow(connection) + ), + &error + ) + ); // Close the connection with the X server CloseConnection(connection); + if (error) + { + err() << "Failed to query pointer" << std::endl; + + return Vector2i(0, 0); + } + return Vector2i(pointer->win_x, pointer->win_y); } else @@ -252,7 +323,21 @@ void InputImpl::setMousePosition(const Vector2i& position) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - xcb_warp_pointer(connection, None, XCBDefaultRootWindow(connection), 0, 0, 0, 0, position.x, position.y); + ScopedXcbPtr error(xcb_request_check( + connection, + xcb_warp_pointer( + connection, + None, // Source window + XCBDefaultRootWindow(connection), // Destination window + 0, 0, // Source position + 0, 0, // Source size + position.x, position.y // Destination position + ) + )); + + if (error) + err() << "Failed to set mouse position" << std::endl; + xcb_flush(connection); // Close the connection with the X server @@ -269,7 +354,21 @@ void InputImpl::setMousePosition(const Vector2i& position, const Window& relativ WindowHandle handle = relativeTo.getSystemHandle(); if (handle) { - xcb_warp_pointer(connection, None, handle, 0, 0, 0, 0, position.x, position.y); + ScopedXcbPtr error(xcb_request_check( + connection, + xcb_warp_pointer( + connection, + None, // Source window + handle, // Destination window + 0, 0, // Source position + 0, 0, // Source size + position.x, position.y // Destination position + ) + )); + + if (error) + err() << "Failed to set mouse position" << std::endl; + xcb_flush(connection); } diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index e69e24a8..a04b42a6 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -31,22 +31,14 @@ #include #include #include -#include #include #include -#include #include -#include -#include #include -#include -#include -#include +#include #include #include -#include -#include -#include +#include #ifdef SFML_OPENGL_ES #include @@ -72,19 +64,34 @@ namespace // Find the name of the current executable std::string findExecutableName() { - struct stat linkStat; - if (!lstat("/proc/self/exe", &linkStat)) { - std::vector buffer(0, linkStat.st_size + 1); - std::size_t length = readlink("/proc/self/exe", &buffer[0], buffer.size()); - if ((length > 0) && (length < buffer.size())) - { - // Remove the path to keep the executable name only - buffer[length] = '\0'; - return basename(&buffer[0]); - } + // We use /proc/self/cmdline to get the command line + // the user used to invoke this instance of the application + int file = ::open("/proc/self/cmdline", O_RDONLY | O_NONBLOCK); + + if (file < 0) + return "sfml"; + + std::vector buffer(256, 0); + std::size_t offset = 0; + ssize_t result = 0; + + while ((result = read(file, &buffer[offset], 256)) > 0) + { + buffer.resize(buffer.size() + result, 0); + offset += result; } - //Default fallback name + ::close(file); + + if (offset) + { + buffer[offset] = 0; + + // Remove the path to keep the executable name only + return basename(&buffer[0]); + } + + // Default fallback name return "sfml"; } @@ -100,103 +107,67 @@ namespace checked = true; xcb_connection_t* connection = sf::priv::OpenConnection(); - - static const std::string NET_SUPPORTING_WM_CHECK = "_NET_SUPPORTING_WM_CHECK"; - sf::priv::ScopedXcbPtr supportingWmAtomReply(xcb_intern_atom_reply( - connection, - xcb_intern_atom( - connection, - 1, - NET_SUPPORTING_WM_CHECK.size(), - NET_SUPPORTING_WM_CHECK.c_str() - ), - NULL - )); - - if (!supportingWmAtomReply) - { - sf::priv::CloseConnection(connection); - return false; - } - - static const std::string NET_SUPPORTED = "_NET_SUPPORTED"; - sf::priv::ScopedXcbPtr supportedAtomReply(xcb_intern_atom_reply( - connection, - xcb_intern_atom( - connection, - 1, - NET_SUPPORTED.size(), - NET_SUPPORTED.c_str() - ), - NULL - )); - - if (!supportedAtomReply) - { - sf::priv::CloseConnection(connection); - return false; - } + xcb_ewmh_connection_t ewmhConnection; sf::priv::ScopedXcbPtr error(NULL); - sf::priv::ScopedXcbPtr rootSupportingWindow(xcb_get_property_reply( - connection, - xcb_get_property( + uint8_t result = xcb_ewmh_init_atoms_replies( + &ewmhConnection, + xcb_ewmh_init_atoms( connection, - 0, - sf::priv::XCBDefaultRootWindow(connection), - supportingWmAtomReply->atom, - XCB_ATOM_WINDOW, - 0, - 0x7fffffff + &ewmhConnection ), &error - )); + ); - if (!rootSupportingWindow || - (rootSupportingWindow->length != 1) || - (rootSupportingWindow->format != 32) || - (rootSupportingWindow->type != XCB_ATOM_WINDOW)) + if (!result || error) { + xcb_ewmh_connection_wipe(&ewmhConnection); sf::priv::CloseConnection(connection); return false; } - xcb_window_t rootWindow = *reinterpret_cast(xcb_get_property_value(rootSupportingWindow.get())); + xcb_window_t rootWindow; - sf::priv::ScopedXcbPtr childSupportingWindow(xcb_get_property_reply( - connection, - xcb_get_property( - connection, - 0, - rootWindow, - supportingWmAtomReply->atom, - XCB_ATOM_WINDOW, - 0, - 0x7fffffff + result = xcb_ewmh_get_supporting_wm_check_reply( + &ewmhConnection, + xcb_ewmh_get_supporting_wm_check( + &ewmhConnection, + sf::priv::XCBDefaultRootWindow(connection) ), + &rootWindow, &error - )); + ); - if (!childSupportingWindow || - (childSupportingWindow->length != 1) || - (childSupportingWindow->format != 32) || - (childSupportingWindow->type != XCB_ATOM_WINDOW)) + if (!result || error) { + xcb_ewmh_connection_wipe(&ewmhConnection); sf::priv::CloseConnection(connection); return false; } - xcb_window_t childWindow = *reinterpret_cast(xcb_get_property_value(childSupportingWindow.get())); + xcb_window_t childWindow; + + result = xcb_ewmh_get_supporting_wm_check_reply( + &ewmhConnection, + xcb_ewmh_get_supporting_wm_check( + &ewmhConnection, + rootWindow + ), + &childWindow, + &error + ); + + xcb_ewmh_connection_wipe(&ewmhConnection); + sf::priv::CloseConnection(connection); + + if (!result || error) + return false; // Conforming window managers should return the same window for both queries - if (rootSupportingWindow != childSupportingWindow) - { - sf::priv::CloseConnection(connection); + if (rootWindow != childWindow) return false; - } - sf::priv::CloseConnection(connection); ewmhSupported = true; return true; } @@ -215,7 +186,6 @@ m_inputContext (NULL), m_isExternal (true), m_atomWmProtocols(0), m_atomClose (0), -m_atomPing (0), m_oldVideoMode (-1), m_hiddenCursor (0), m_keyRepeat (true), @@ -234,7 +204,22 @@ m_fullscreen (false) } // Make sure to check for EWMH support before we do anything - ewmhSupported(); + if (ewmhSupported()) + { + ScopedXcbPtr error(NULL); + + uint8_t result = xcb_ewmh_init_atoms_replies( + &m_ewmhConnection, + xcb_ewmh_init_atoms( + m_connection, + &m_ewmhConnection + ), + &error + ); + + if (!result || error) + err() << "Failed to initialize EWMH atoms" << std::endl; + } m_screen = XCBDefaultScreen(m_connection); XSetEventQueueOwner(m_display, XCBOwnsEventQueue); @@ -254,6 +239,9 @@ m_fullscreen (false) value_list ); + // Set the WM protocols + setProtocols(); + // Do some common initializations initialize(); } @@ -268,7 +256,6 @@ m_inputContext (NULL), m_isExternal (false), m_atomWmProtocols(0), m_atomClose (0), -m_atomPing (0), m_oldVideoMode (-1), m_hiddenCursor (0), m_keyRepeat (true), @@ -287,7 +274,22 @@ m_fullscreen ((style & Style::Fullscreen) != 0) } // Make sure to check for EWMH support before we do anything - ewmhSupported(); + if (ewmhSupported()) + { + ScopedXcbPtr error(NULL); + + uint8_t result = xcb_ewmh_init_atoms_replies( + &m_ewmhConnection, + xcb_ewmh_init_atoms( + m_connection, + &m_ewmhConnection + ), + &error + ); + + if (!result || error) + err() << "Failed to initialize EWMH atoms" << std::endl; + } m_screen = XCBDefaultScreen(m_connection); XSetEventQueueOwner(m_display, XCBOwnsEventQueue); @@ -332,107 +334,55 @@ m_fullscreen ((style & Style::Fullscreen) != 0) return; } - // Set the window's name - setTitle(title); + // Set the WM protocols + setProtocols(); + + // Set the WM initial state to the normal state + xcb_icccm_wm_hints_t hints; + std::memset(&hints, 0, sizeof(hints)); + xcb_icccm_wm_hints_set_normal(&hints); + xcb_icccm_set_wm_hints(m_connection, m_window, &hints); + + xcb_size_hints_t sizeHints; + 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) { - static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS"; - ScopedXcbPtr hintsAtomReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - MOTIF_WM_HINTS.size(), - MOTIF_WM_HINTS.c_str() - ), - NULL - )); - - if (hintsAtomReply) - { - static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0; - static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1; - - //static const unsigned long MWM_DECOR_ALL = 1 << 0; - static const unsigned long MWM_DECOR_BORDER = 1 << 1; - static const unsigned long MWM_DECOR_RESIZEH = 1 << 2; - static const unsigned long MWM_DECOR_TITLE = 1 << 3; - static const unsigned long MWM_DECOR_MENU = 1 << 4; - static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5; - static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6; - - //static const unsigned long MWM_FUNC_ALL = 1 << 0; - static const unsigned long MWM_FUNC_RESIZE = 1 << 1; - static const unsigned long MWM_FUNC_MOVE = 1 << 2; - static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3; - static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4; - static const unsigned long MWM_FUNC_CLOSE = 1 << 5; - - struct WMHints - { - uint32_t flags; - uint32_t functions; - uint32_t decorations; - int32_t inputMode; - uint32_t state; - }; - - WMHints hints; - hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; - hints.decorations = 0; - hints.functions = 0; - - 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( - m_connection, - xcb_change_property_checked( - m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - hintsAtomReply->atom, - hintsAtomReply->atom, - sizeof(hints.flags) * 8, - sizeof(hints) / sizeof(hints.flags), - reinterpret_cast(&hints) - ) - )); - - if (propertyError) - err() << "xcb_change_property failed, could not set window hints" << std::endl; - } + // Set the Motif WM hints + setMotifHints(style); // This is a hack to force some windows managers to disable resizing if (!(style & Style::Resize)) { m_useSizeHints = true; - xcb_size_hints_t sizeHints; - sizeHints.flags = XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE; - sizeHints.min_width = sizeHints.max_width = width; - sizeHints.min_height = sizeHints.max_height = height; - xcb_icccm_set_wm_normal_hints(m_connection, m_window, &sizeHints); + 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 + xcb_icccm_set_wm_normal_hints(m_connection, m_window, &sizeHints); + // Set the window's WM class (this can be used by window managers) + // The WM_CLASS property actually consists of 2 parts, + // the instance name and the class name both of which should be + // null terminated strings. + // The instance name should be something unique to this invokation + // of the application but is rarely if ever used these days. + // For simplicity, we retrieve it via the base executable name. + // The class name identifies a class of windows that + // "are of the same type". We simply use the initial window name as + // the class name. std::string windowClass = findExecutableName(); - xcb_icccm_set_wm_class_checked(m_connection, m_window, windowClass.size(), windowClass.c_str()); + windowClass += '\0'; // Important to separate instance from class + windowClass += title.toAnsiString(); + + // We add 1 to the size of the string to include the null at the end + xcb_icccm_set_wm_class_checked(m_connection, m_window, windowClass.size() + 1, windowClass.c_str()); + + // Set the window's name + setTitle(title); // Do some common initializations initialize(); @@ -472,6 +422,9 @@ WindowImplX11::~WindowImplX11() if (m_inputMethod) XCloseIM(m_inputMethod); + // Clean up the EWMH connection + xcb_ewmh_connection_wipe(&m_ewmhConnection); + // Close the connection with the X server CloseDisplay(m_display); @@ -487,44 +440,6 @@ WindowHandle WindowImplX11::getSystemHandle() const } -//////////////////////////////////////////////////////////// -void WindowImplX11::setPointerGrabbed(bool grabbed) -{ - // NOTE: This only works when the window is mapped and visible! - - if (grabbed) - { - ScopedXcbPtr grabReply(xcb_grab_pointer_reply( - m_connection, - xcb_grab_pointer( - m_connection, - 1, - m_screen->root, - XCB_NONE, - XCB_GRAB_MODE_ASYNC, - XCB_GRAB_MODE_ASYNC, - m_window, - XCB_NONE, - XCB_CURRENT_TIME - ), - NULL - )); - - if (grabReply && grabReply->status != 0) - err() << "Grabbing the pointer failed." << std::endl - << " Status: " - << static_cast(grabReply->status) << std::endl; - else if (!grabReply) - err() << "Grabbing the pointer failed." << std::endl; - } - else - { - xcb_ungrab_pointer(m_connection, XCB_CURRENT_TIME); - xcb_flush(m_connection); - } -} - - //////////////////////////////////////////////////////////// void WindowImplX11::processEvents() { @@ -593,12 +508,28 @@ Vector2i WindowImplX11::getPosition() const ::Window topLevelWindow = m_window; ::Window nextWindow = topLevelWindow; + ScopedXcbPtr error(NULL); + // Get "top level" window, i.e. the window with the root window as its parent. while (nextWindow != m_screen->root) { topLevelWindow = nextWindow; - ScopedXcbPtr treeReply(xcb_query_tree_reply(m_connection, xcb_query_tree(m_connection, topLevelWindow), NULL)); + ScopedXcbPtr treeReply(xcb_query_tree_reply( + m_connection, + xcb_query_tree( + m_connection, + topLevelWindow + ), + &error + )); + + if (error) + { + err() << "Failed to get window position (query_tree)" << std::endl; + return Vector2i(0, 0); + } + nextWindow = treeReply->parent; } @@ -608,10 +539,16 @@ Vector2i WindowImplX11::getPosition() const m_connection, topLevelWindow ), - NULL + &error )); - return sf::Vector2i(geometryReply->x, geometryReply->y); + if (error) + { + err() << "Failed to get window position (get_geometry)" << std::endl; + return Vector2i(0, 0); + } + + return Vector2i(geometryReply->x, geometryReply->y); } @@ -641,19 +578,38 @@ void WindowImplX11::setSize(const Vector2u& size) // If resizing is disable for the window we have to update the size hints (required by some window managers). if( m_useSizeHints ) { xcb_size_hints_t sizeHints; - sizeHints.flags = XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE; - sizeHints.min_width = sizeHints.max_width = size.x; - sizeHints.min_height = sizeHints.max_height = size.y; - xcb_icccm_set_wm_normal_hints(m_connection, m_window, &sizeHints); + std::memset(&sizeHints, 0, sizeof(sizeHints)); + xcb_icccm_size_hints_set_min_size(&sizeHints, size.x, size.y); + xcb_icccm_size_hints_set_max_size(&sizeHints, size.x, size.y); + + ScopedXcbPtr configureWindowError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_normal_hints( + m_connection, + m_window, + &sizeHints + ) + )); + + if (configureWindowError) + err() << "Failed to set window size hints" << std::endl; } uint32_t values[] = {size.x, size.y}; - xcb_configure_window( + + ScopedXcbPtr configureWindowError(xcb_request_check( m_connection, - m_window, - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, - values - ); + xcb_configure_window( + m_connection, + m_window, + XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, + values + ) + )); + + if (configureWindowError) + err() << "Failed to set window size" << std::endl; + xcb_flush(m_connection); } @@ -662,22 +618,68 @@ void WindowImplX11::setSize(const Vector2u& size) void WindowImplX11::setTitle(const String& title) { // XCB takes UTF-8-encoded strings. - std::basic_string utf8String; - sf::Utf<32>::toUtf8( - title.begin(), title.end(), - std::back_inserter( utf8String ) - ); + std::string utf8String; + Utf<32>::toUtf8(title.begin(), title.end(), std::back_inserter(utf8String)); - xcb_change_property( + ScopedXcbPtr wmNameError(xcb_request_check( m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - XCB_ATOM_WM_NAME, - XCB_ATOM_STRING, - sizeof(std::basic_string::value_type) * 8, - utf8String.length(), - utf8String.c_str() - ); + xcb_icccm_set_wm_name_checked( + m_connection, + m_window, + XCB_ATOM_STRING, + sizeof(std::basic_string::value_type) * 8, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (wmNameError) + err() << "Failed to set window title" << std::endl; + + ScopedXcbPtr wmIconNameError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_icon_name_checked( + m_connection, + m_window, + XCB_ATOM_STRING, + sizeof(std::basic_string::value_type) * 8, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (wmIconNameError) + err() << "Failed to set WM_ICON_NAME property" << std::endl; + + if (ewmhSupported()) + { + ScopedXcbPtr ewmhNameError(xcb_request_check( + m_connection, + xcb_ewmh_set_wm_name_checked( + &m_ewmhConnection, + m_window, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (ewmhNameError) + err() << "Failed to set _NET_WM_NAME property" << std::endl; + + ScopedXcbPtr ewmhIconNameError(xcb_request_check( + m_connection, + xcb_ewmh_set_wm_icon_name_checked( + &m_ewmhConnection, + m_window, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (ewmhIconNameError) + err() << "Failed to set _NET_WM_ICON_NAME property" << std::endl; + } + xcb_flush(m_connection); } @@ -697,19 +699,47 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 // Create the icon pixmap xcb_pixmap_t iconPixmap = xcb_generate_id(m_connection); - xcb_create_pixmap( + + ScopedXcbPtr createPixmapError(xcb_request_check( m_connection, - m_screen->root_depth, - iconPixmap, - m_screen->root, - width, - height - ); + xcb_create_pixmap_checked( + m_connection, + m_screen->root_depth, + iconPixmap, + m_screen->root, + width, + height + ) + )); + + if (createPixmapError) + { + err() << "Failed to set the window's icon (create_pixmap): "; + err() << "Error code " << static_cast(createPixmapError->error_code) << std::endl; + return; + } xcb_gcontext_t iconGC = xcb_generate_id(m_connection); - xcb_create_gc(m_connection, iconGC, iconPixmap, 0, NULL); - ScopedXcbPtr errptr(xcb_request_check( + ScopedXcbPtr createGcError(xcb_request_check( + m_connection, + xcb_create_gc( + m_connection, + iconGC, + iconPixmap, + 0, + NULL + ) + )); + + if (createGcError) + { + err() << "Failed to set the window's icon (create_gc): "; + err() << "Error code " << static_cast(createGcError->error_code) << std::endl; + return; + } + + ScopedXcbPtr putImageError(xcb_request_check( m_connection, xcb_put_image_checked( m_connection, @@ -727,11 +757,24 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 ) )); - xcb_free_gc(m_connection, iconGC); + ScopedXcbPtr freeGcError(xcb_request_check( + m_connection, + xcb_free_gc( + m_connection, + iconGC + ) + )); - if (errptr) + if (freeGcError) { - err() << "Failed to set the window's icon: Error code " << static_cast(errptr->error_code) << std::endl; + err() << "Failed to free icon GC: "; + err() << "Error code " << static_cast(freeGcError->error_code) << std::endl; + } + + if (putImageError) + { + err() << "Failed to set the window's icon (put_image): "; + err() << "Error code " << static_cast(putImageError->error_code) << std::endl; return; } @@ -767,12 +810,40 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 // Send our new icon to the window through the WMHints xcb_icccm_wm_hints_t hints; - hints.flags = XCB_ICCCM_WM_HINT_ICON_PIXMAP | XCB_ICCCM_WM_HINT_ICON_MASK; - hints.icon_pixmap = iconPixmap; - hints.icon_mask = maskPixmap; - xcb_icccm_set_wm_hints(m_connection, m_window, &hints); + std::memset(&hints, 0, sizeof(hints)); + xcb_icccm_wm_hints_set_icon_pixmap(&hints, iconPixmap); + xcb_icccm_wm_hints_set_icon_mask(&hints, maskPixmap); + + ScopedXcbPtr setWmHintsError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_hints( + m_connection, + m_window, + &hints + ) + )); + + if (setWmHintsError) + { + err() << "Failed to set the window's icon (icccm_set_wm_hints): "; + err() << "Error code " << static_cast(setWmHintsError->error_code) << std::endl; + } xcb_flush(m_connection); + + ScopedXcbPtr freePixmapError(xcb_request_check( + m_connection, + xcb_free_pixmap_checked( + m_connection, + iconPixmap + ) + )); + + if (freePixmapError) + { + err() << "Failed to free icon pixmap: "; + err() << "Error code " << static_cast(freePixmapError->error_code) << std::endl; + } } @@ -780,9 +851,31 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 void WindowImplX11::setVisible(bool visible) { if (visible) - xcb_map_window(m_connection, m_window); + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_map_window( + m_connection, + m_window + ) + )); + + if (error) + err() << "Failed to change window visibility" << std::endl; + } else - xcb_unmap_window(m_connection, m_window); + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_unmap_window( + m_connection, + m_window + ) + )); + + if (error) + err() << "Failed to change window visibility" << std::endl; + } xcb_flush(m_connection); } @@ -792,7 +885,20 @@ void WindowImplX11::setVisible(bool visible) void WindowImplX11::setMouseCursorVisible(bool visible) { const uint32_t values = visible ? XCB_NONE : m_hiddenCursor; - xcb_change_window_attributes(m_connection, m_window, XCB_CW_CURSOR, &values); + + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_change_window_attributes( + m_connection, + m_window, + XCB_CW_CURSOR, + &values + ) + )); + + if (error) + err() << "Failed to change mouse cursor visibility" << std::endl; + xcb_flush(m_connection); } @@ -843,14 +949,14 @@ void WindowImplX11::requestFocus() { // Another SFML window of this application has the focus and the current window is viewable: // steal focus (i.e. bring window to the front and give it input focus) - xcb_set_input_focus(m_connection, XCB_INPUT_FOCUS_POINTER_ROOT, m_window, XCB_CURRENT_TIME); - const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; - xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); + grabFocus(); } else { // Get current WM hints. xcb_icccm_wm_hints_t hints; + std::memset(&hints, 0, sizeof(hints)); + xcb_icccm_get_wm_hints_reply( m_connection, xcb_icccm_get_wm_hints_unchecked( @@ -863,7 +969,7 @@ void WindowImplX11::requestFocus() // Even if no hints were returned, we can simply set the proper flags we need and go on. This is // different from Xlib where XAllocWMHints() has to be called. - hints.flags |= XCB_ICCCM_WM_HINT_X_URGENCY; + xcb_icccm_wm_hints_set_urgency(&hints); xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); } } @@ -872,18 +978,82 @@ void WindowImplX11::requestFocus() //////////////////////////////////////////////////////////// bool WindowImplX11::hasFocus() const { + ScopedXcbPtr error(NULL); + ScopedXcbPtr reply(xcb_get_input_focus_reply( m_connection, xcb_get_input_focus_unchecked( m_connection ), - NULL + &error )); + if (error) + err() << "Failed to check if window has focus" << std::endl; + return (reply->focus == m_window); } +//////////////////////////////////////////////////////////// +void WindowImplX11::grabFocus() +{ + if (ewmhSupported()) + { + ScopedXcbPtr activeWindowError(xcb_request_check( + m_connection, + xcb_ewmh_request_change_active_window( + &m_ewmhConnection, + XDefaultScreen(m_display), + m_window, + XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL, + XCB_CURRENT_TIME, + None + ) + )); + + if (activeWindowError) + { + err() << "Failed to change active window (request_change_active_window)" << std::endl; + return; + } + } + else + { + ScopedXcbPtr setInputFocusError(xcb_request_check( + m_connection, + xcb_set_input_focus( + m_connection, + XCB_INPUT_FOCUS_POINTER_ROOT, + m_window, + XCB_CURRENT_TIME + ) + )); + + if (setInputFocusError) + { + err() << "Failed to change active window (set_input_focus)" << std::endl; + return; + } + + const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; + + ScopedXcbPtr configureWindowError(xcb_request_check( + m_connection, + xcb_configure_window( + m_connection, + m_window, + XCB_CONFIG_WINDOW_STACK_MODE, + values + ) + )); + + if (configureWindowError) + err() << "Failed to change active window (configure_window)" << std::endl; + } +} + + //////////////////////////////////////////////////////////// void WindowImplX11::setVideoMode(const VideoMode& mode) { @@ -1042,7 +1212,8 @@ void WindowImplX11::resetVideoMode() //////////////////////////////////////////////////////////// void WindowImplX11::switchToFullscreen() { - // Try EWMH method if supported or fallback to manual + grabFocus(); + if (ewmhSupported()) { // Create atom for _NET_WM_BYPASS_COMPOSITOR. @@ -1051,7 +1222,7 @@ void WindowImplX11::switchToFullscreen() m_connection, xcb_intern_atom( m_connection, - 0, + False, netWmStateBypassCompositor.size(), netWmStateBypassCompositor.c_str() ), @@ -1079,141 +1250,32 @@ void WindowImplX11::switchToFullscreen() if (compositorError) err() << "xcb_change_property failed, unable to set _NET_WM_BYPASS_COMPOSITOR" << std::endl; - // Create atom for _NET_ACTIVE_WINDOW. - static const std::string netActiveWindow = "_NET_ACTIVE_WINDOW"; - ScopedXcbPtr activeWindowReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - netActiveWindow.size(), - netActiveWindow.c_str() - ), - 0 - )); - - if (!activeWindowReply) - { - err() << "Setting fullscreen failed, could not get \"_NET_ACTIVE_WINDOW\" atom" << std::endl; - return; - } - - xcb_client_message_event_t activeWindowEvent; - std::memset(&activeWindowEvent, 0, sizeof(activeWindowEvent)); - - activeWindowEvent.response_type = XCB_CLIENT_MESSAGE; - activeWindowEvent.window = m_window; - activeWindowEvent.format = 32; - activeWindowEvent.type = activeWindowReply->atom; - activeWindowEvent.data.data32[0] = 1; - activeWindowEvent.data.data32[1] = XCB_CURRENT_TIME; - - ScopedXcbPtr activeWindowError(xcb_request_check( - m_connection, - xcb_send_event_checked( - m_connection, - 0, - XCBDefaultRootWindow(m_connection), - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - reinterpret_cast(&activeWindowEvent) - ) - )); - - if (activeWindowError) - { - err() << "Setting fullscreen failed, could not send \"_NET_ACTIVE_WINDOW\" event" << std::endl; - return; - } - - // Create atom for _NET_WM_STATE. - static const std::string netWmState = "_NET_WM_STATE"; - ScopedXcbPtr stateReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - netWmState.size(), - netWmState.c_str() - ), - 0 - )); - - if (!stateReply) - { - err() << "Setting fullscreen failed. Could not get \"_NET_WM_STATE\" atom" << std::endl; - return; - } - - // Create atom for _NET_WM_STATE_FULLSCREEN. - static const std::string netWmStateFullscreen = "_NET_WM_STATE_FULLSCREEN"; - ScopedXcbPtr fullscreenReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - netWmStateFullscreen.size(), - netWmStateFullscreen.c_str() - ), - 0 - )); - - if (!fullscreenReply) - { - err() << "Setting fullscreen failed. Could not get \"_NET_WM_STATE_FULLSCREEN\" atom" << std::endl; - return; - } - - xcb_client_message_event_t wmStateEvent; - std::memset(&wmStateEvent, 0, sizeof(wmStateEvent)); - - wmStateEvent.response_type = XCB_CLIENT_MESSAGE; - wmStateEvent.window = m_window; - wmStateEvent.format = 32; - wmStateEvent.type = stateReply->atom; - wmStateEvent.data.data32[0] = 1; // _NET_WM_STATE_ADD - wmStateEvent.data.data32[1] = fullscreenReply->atom; - wmStateEvent.data.data32[2] = 0; - wmStateEvent.data.data32[3] = 1; - ScopedXcbPtr wmStateError(xcb_request_check( m_connection, - xcb_send_event_checked( - m_connection, + xcb_ewmh_request_change_wm_state( + &m_ewmhConnection, + XDefaultScreen(m_display), + m_window, + XCB_EWMH_WM_STATE_ADD, + m_ewmhConnection._NET_WM_STATE_FULLSCREEN, 0, - XCBDefaultRootWindow(m_connection), - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - reinterpret_cast(&wmStateEvent) + XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL ) )); if (wmStateError) - err() << "Setting fullscreen failed. Could not send \"_NET_WM_STATE\" event" << std::endl; - } - else - { - // If WM fullscreen setting is not available, we need to do it manually - const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; - xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); - xcb_set_input_focus(m_connection, XCB_INPUT_FOCUS_POINTER_ROOT, m_window, XCB_CURRENT_TIME); + { + err() << "Setting fullscreen failed, could not change WM state" << std::endl; + return; + } } } //////////////////////////////////////////////////////////// -void WindowImplX11::initialize() +void WindowImplX11::setProtocols() { - // Get the atom for registering the close event - static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; - ScopedXcbPtr deleteWindowAtomReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - WM_DELETE_WINDOW_NAME.size(), - WM_DELETE_WINDOW_NAME.c_str() - ), - NULL - )); + ScopedXcbPtr error(NULL); // Get the atom for setting the window protocols we support static const std::string WM_PROTOCOLS_NAME = "WM_PROTOCOLS"; @@ -1225,109 +1287,174 @@ void WindowImplX11::initialize() WM_PROTOCOLS_NAME.size(), WM_PROTOCOLS_NAME.c_str() ), - NULL + &error )); - // Get the atom for registering the ping event - static const std::string NET_WM_PING = "_NET_WM_PING"; - sf::priv::ScopedXcbPtr wmPingReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - NET_WM_PING.size(), - NET_WM_PING.c_str() - ), - NULL - )); - - // Get the atom for setting the process ID of the window - static const std::string NET_WM_PID = "_NET_WM_PID"; - sf::priv::ScopedXcbPtr wmPidReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - NET_WM_PID.size(), - NET_WM_PID.c_str() - ), - NULL - )); - - if (protocolsAtomReply) + if (error || !protocolsAtomReply) { - m_atomWmProtocols = protocolsAtomReply->atom; + err() << "Failed to request WM_PROTOCOLS atom." << std::endl; + return; + } - xcb_atom_t atoms[2]; - Uint32 atomCount = 0; + m_atomWmProtocols = protocolsAtomReply->atom; - if (deleteWindowAtomReply && ewmhSupported() && wmPingReply && wmPidReply) - { - atoms[0] = deleteWindowAtomReply->atom; - atomCount = 1; + std::vector atoms; - m_atomClose = deleteWindowAtomReply->atom; + // Get the atom for registering the close event + static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; + ScopedXcbPtr deleteWindowAtomReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + WM_DELETE_WINDOW_NAME.size(), + WM_DELETE_WINDOW_NAME.c_str() + ), + &error + )); - Uint32 pid = getpid(); - - // Set PID. - ScopedXcbPtr pidError(xcb_request_check( - m_connection, - xcb_change_property_checked( - m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - wmPidReply->atom, - XCB_ATOM_CARDINAL, - 32, - 1, - &pid - ) - )); - - if (!pidError) - { - atoms[1] = wmPingReply->atom; - atomCount = 2; - - m_atomPing = wmPingReply->atom; - } - } - else if (deleteWindowAtomReply) - { - atoms[0] = deleteWindowAtomReply->atom; - atomCount = 1; - - m_atomClose = deleteWindowAtomReply->atom; - } - - if (atomCount) - { - ScopedXcbPtr setProtocolsError(xcb_request_check( - m_connection, - xcb_icccm_set_wm_protocols( - m_connection, - m_window, - protocolsAtomReply->atom, - atomCount, - atoms - ) - )); - - if (setProtocolsError) - err() << "Failed to set window protocols" << std::endl; - } - else - { - err() << "Didn't set any window protocols" << std::endl; - } + if (!error && deleteWindowAtomReply) + { + atoms.push_back(deleteWindowAtomReply->atom); + m_atomClose = deleteWindowAtomReply->atom; } else { - // Should not happen, but better safe than sorry. - err() << "Failed to request WM_PROTOCOLS atoms." << std::endl; + err() << "Failed to request WM_DELETE_WINDOW atom." << std::endl; } + if (ewmhSupported() && m_ewmhConnection._NET_WM_PING && m_ewmhConnection._NET_WM_PID) + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_ewmh_set_wm_pid( + &m_ewmhConnection, + m_window, + getpid() + ) + )); + + if (!error) + atoms.push_back(m_ewmhConnection._NET_WM_PING); + } + + if (!atoms.empty()) + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_icccm_set_wm_protocols( + m_connection, + m_window, + protocolsAtomReply->atom, + atoms.size(), + &atoms[0] + ) + )); + + if (error) + err() << "Failed to set window protocols" << std::endl; + } + else + { + err() << "Didn't set any window protocols" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::setMotifHints(unsigned long style) +{ + ScopedXcbPtr error(NULL); + + static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS"; + ScopedXcbPtr hintsAtomReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + MOTIF_WM_HINTS.size(), + MOTIF_WM_HINTS.c_str() + ), + &error + )); + + if (!error && hintsAtomReply) + { + static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0; + static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1; + + //static const unsigned long MWM_DECOR_ALL = 1 << 0; + static const unsigned long MWM_DECOR_BORDER = 1 << 1; + static const unsigned long MWM_DECOR_RESIZEH = 1 << 2; + static const unsigned long MWM_DECOR_TITLE = 1 << 3; + static const unsigned long MWM_DECOR_MENU = 1 << 4; + static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5; + static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6; + + //static const unsigned long MWM_FUNC_ALL = 1 << 0; + static const unsigned long MWM_FUNC_RESIZE = 1 << 1; + static const unsigned long MWM_FUNC_MOVE = 1 << 2; + static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3; + static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4; + static const unsigned long MWM_FUNC_CLOSE = 1 << 5; + + struct WMHints + { + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t inputMode; + uint32_t state; + }; + + WMHints hints; + hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; + hints.decorations = 0; + hints.functions = 0; + + 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( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + hintsAtomReply->atom, + hintsAtomReply->atom, + sizeof(hints.flags) * 8, + sizeof(hints) / sizeof(hints.flags), + reinterpret_cast(&hints) + ) + )); + + if (propertyError) + err() << "xcb_change_property failed, could not set window hints" << std::endl; + } + else + { + err() << "Failed to request _MOTIF_WM_HINTS atom." << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::initialize() +{ // Create the input context m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL); @@ -1353,14 +1480,10 @@ void WindowImplX11::initialize() err() << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl; // Show the window - xcb_map_window(m_connection, m_window); + setVisible(true); // Raise the window and grab input focus - xcb_set_input_focus(m_connection, XCB_INPUT_FOCUS_POINTER_ROOT, m_window, XCB_CURRENT_TIME); - const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; - xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); - - xcb_flush(m_connection); + grabFocus(); // Create the hidden cursor createHiddenCursor(); @@ -1377,24 +1500,57 @@ void WindowImplX11::initialize() void WindowImplX11::createHiddenCursor() { xcb_pixmap_t cursorPixmap = xcb_generate_id(m_connection); - m_hiddenCursor = xcb_generate_id(m_connection); + // Create the cursor's pixmap (1x1 pixels) - xcb_create_pixmap(m_connection, 1, cursorPixmap, m_window, 1, 1); + ScopedXcbPtr createPixmapError(xcb_request_check( + m_connection, + xcb_create_pixmap( + m_connection, + 1, + cursorPixmap, + m_window, + 1, + 1 + ) + )); + + if (createPixmapError) + { + err() << "Failed to create pixmap for hidden cursor" << std::endl; + return; + } + + m_hiddenCursor = xcb_generate_id(m_connection); // Create the cursor, using the pixmap as both the shape and the mask of the cursor - xcb_create_cursor( + ScopedXcbPtr createCursorError(xcb_request_check( m_connection, - m_hiddenCursor, - cursorPixmap, - cursorPixmap, - 0, 0, 0, // Foreground RGB color - 0, 0, 0, // Background RGB color - 0, // X - 0 // Y - ); + xcb_create_cursor( + m_connection, + m_hiddenCursor, + cursorPixmap, + cursorPixmap, + 0, 0, 0, // Foreground RGB color + 0, 0, 0, // Background RGB color + 0, // X + 0 // Y + ) + )); + + if (createCursorError) + err() << "Failed to create hidden cursor" << std::endl; // We don't need the pixmap any longer, free it - xcb_free_pixmap(m_connection, cursorPixmap); + ScopedXcbPtr freePixmapError(xcb_request_check( + m_connection, + xcb_free_pixmap( + m_connection, + cursorPixmap + ) + )); + + if (freePixmapError) + err() << "Failed to free pixmap for hidden cursor" << std::endl; } @@ -1436,23 +1592,39 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // If the window has been previously marked urgent (notification) as a result of a focus request, undo that xcb_icccm_wm_hints_t hints; - xcb_icccm_get_wm_hints_reply( + + ScopedXcbPtr error(NULL); + + uint8_t result = xcb_icccm_get_wm_hints_reply( m_connection, xcb_icccm_get_wm_hints_unchecked( m_connection, m_window ), &hints, - NULL + &error ); - // Remove urgency (notification) flag from hints - hints.flags &= ~XUrgencyHint; - xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); + if (!result || error) + { + err() << "Failed to get WM hints in XCB_FOCUS_IN" << std::endl; + break; + } - // Grab pointer if necessary. - if (m_fullscreen) - setPointerGrabbed(true); + // Remove urgency (notification) flag from hints + hints.flags &= ~XCB_ICCCM_WM_HINT_X_URGENCY; + + ScopedXcbPtr setWmHintsError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_hints_checked( + m_connection, + m_window, + &hints + ) + )); + + if (setWmHintsError) + err() << "Failed to set WM hints in XCB_FOCUS_IN" << std::endl; break; } @@ -1460,10 +1632,6 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Lost focus event case XCB_FOCUS_OUT: { - // Ungrab pointer if necessary. - if (m_fullscreen) - setPointerGrabbed(false); - // Update the input context if (m_inputContext) XUnsetICFocus(m_inputContext); @@ -1501,7 +1669,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) event.type = Event::Closed; pushEvent(event); } - else if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomPing)) + else if (ewmhSupported() && (e->format == 32) && (e->data.data32[0]) == static_cast(m_ewmhConnection._NET_WM_PING)) { // Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive e->window = XCBDefaultRootWindow(m_connection); @@ -1763,6 +1931,12 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Parent window changed case XCB_REPARENT_NOTIFY: { + // Catch reparent events to properly apply fullscreen on + // some "strange" window managers (like Awesome) which + // seem to make use of temporary parents during mapping + if (m_fullscreen) + switchToFullscreen(); + xcb_flush(m_connection); // Discard remaining events break; } diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index b5aa1900..575942c8 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -179,6 +180,12 @@ protected: private: + //////////////////////////////////////////////////////////// + /// \brief Request the WM to make the current window active + /// + //////////////////////////////////////////////////////////// + void grabFocus(); + //////////////////////////////////////////////////////////// /// \brief Set fullscreen video mode /// @@ -200,12 +207,16 @@ private: void switchToFullscreen(); //////////////////////////////////////////////////////////// - /// \brief Grab or ungrab mouse pointer. - /// - /// \param grabbed True to grab, false to ungrab. + /// \brief Set the WM protocols we support /// //////////////////////////////////////////////////////////// - void setPointerGrabbed(bool grabbed); + void setProtocols(); + + //////////////////////////////////////////////////////////// + /// \brief Set Motif WM hints + /// + //////////////////////////////////////////////////////////// + void setMotifHints(unsigned long style); //////////////////////////////////////////////////////////// /// \brief Do some common initializations after the window has been created @@ -248,22 +259,22 @@ 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_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 - Atom m_atomPing; ///< Atom used to identify the ping 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? + ::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? }; } // namespace priv From bd34935f2a0526ee35c5bb277c98f9089b5fb177 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Wed, 25 Mar 2015 22:08:50 +0100 Subject: [PATCH 5/8] 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 From 39cdebfe714299af7143e0140e1912ffa84e5fa5 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Fri, 27 Mar 2015 16:53:24 +0100 Subject: [PATCH 6/8] 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 0e13ff7b..d5ab805e 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 9ac297be..9ee812f2 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 b826b70a..1e176221 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; From 5aaf9a611535dc408e4301ed82256685fde66878 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Fri, 27 Mar 2015 18:31:41 +0100 Subject: [PATCH 7/8] Fixed RandR not resetting the original screen configuration in some situations. --- src/SFML/Window/Unix/WindowImplX11.cpp | 51 +++++++++++--------------- src/SFML/Window/Unix/WindowImplX11.hpp | 35 +++++++++--------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 1e176221..b05e88dd 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -311,7 +311,6 @@ m_inputContext (NULL), m_isExternal (true), m_atomWmProtocols(0), m_atomClose (0), -m_oldVideoMode (-1), m_hiddenCursor (0), m_keyRepeat (true), m_previousSize (-1, -1), @@ -322,6 +321,8 @@ m_fullscreen (false) m_display = OpenDisplay(); m_connection = XGetXCBConnection(m_display); + std::memset(&m_oldVideoMode, 0, sizeof(m_oldVideoMode)); + if (!m_connection) { err() << "Failed cast Display object to an XCB connection object" << std::endl; @@ -381,7 +382,6 @@ m_inputContext (NULL), m_isExternal (false), m_atomWmProtocols(0), m_atomClose (0), -m_oldVideoMode (-1), m_hiddenCursor (0), m_keyRepeat (true), m_previousSize (-1, -1), @@ -392,6 +392,8 @@ m_fullscreen ((style & Style::Fullscreen) != 0) m_display = OpenDisplay(); m_connection = XGetXCBConnection(m_display); + std::memset(&m_oldVideoMode, 0, sizeof(m_oldVideoMode)); + if (!m_connection) { err() << "Failed cast Display object to an XCB connection object" << std::endl; @@ -1252,7 +1254,7 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) &error )); - if (error) + if (error || !config) { // Failed to get the screen configuration err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; @@ -1260,7 +1262,7 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) } // Save the current video mode before we switch to fullscreen - m_oldVideoMode = config->sizeID; + m_oldVideoMode = *config.get(); // Get the available screen sizes xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); @@ -1286,12 +1288,12 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) m_connection, xcb_randr_set_screen_config( m_connection, - m_screen->root, + config->root, XCB_CURRENT_TIME, config->config_timestamp, i, config->rotation, - 0//config->rate + config->rate ), &error )); @@ -1316,35 +1318,24 @@ void WindowImplX11::resetVideoMode() { // Get current screen info ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + + // Reset the video mode + ScopedXcbPtr setScreenConfig(xcb_randr_set_screen_config_reply( m_connection, - xcb_randr_get_screen_info( - m_connection, - m_screen->root + xcb_randr_set_screen_config( + m_connection, + m_oldVideoMode.root, + XCB_CURRENT_TIME, + m_oldVideoMode.config_timestamp, + m_oldVideoMode.sizeID, + m_oldVideoMode.rotation, + m_oldVideoMode.rate ), &error )); - if (!error) - { - // Reset the video mode - ScopedXcbPtr setScreenConfig(xcb_randr_set_screen_config_reply( - m_connection, - xcb_randr_set_screen_config( - m_connection, - m_screen->root, - CurrentTime, - config->config_timestamp, - m_oldVideoMode, - config->rotation, - config->rate - ), - &error - )); - - if (error) - err() << "Failed to reset old screen configuration" << std::endl; - } + if (error) + err() << "Failed to reset old screen configuration" << std::endl; // Reset the fullscreen window fullscreenWindow = NULL; diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index b3918072..5bd596dd 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -270,23 +271,23 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - 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? + 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 + xcb_randr_get_screen_info_reply_t 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 From 7287b77c5df40fad243b68fc72759272d400ff99 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Sat, 28 Mar 2015 13:51:42 +0100 Subject: [PATCH 8/8] Replaced a few Xlib keyboard handling functions with XCB keyboard handling functions, fixed modified key events returning sf::Keyboard::Unknown on Unix (#847), fixed sf::Keyboard::Quote and sf::Keyboard::Tilde events not functioning properly on Unix, optimized keycode lookup when using sf::Keyboard::isKeyPressed() on Unix. --- src/SFML/Window/CMakeLists.txt | 2 +- src/SFML/Window/Unix/InputImpl.cpp | 317 ++++++++++++++----------- src/SFML/Window/Unix/WindowImplX11.cpp | 57 +++-- src/SFML/Window/Unix/WindowImplX11.hpp | 2 + 4 files changed, 208 insertions(+), 170 deletions(-) diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index d5ab805e..e39fcd3c 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -199,7 +199,7 @@ if(NOT SFML_OPENGL_ES) find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) if(SFML_OS_LINUX OR SFML_OS_FREEBSD) - find_package(XCB COMPONENTS xlib_xcb icccm image randr util ewmh REQUIRED) + find_package(XCB COMPONENTS xlib_xcb icccm image randr util ewmh keysyms REQUIRED) if(NOT LIBXCB_FOUND) message(FATAL_ERROR "Xcb library not found") endif() diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index ee4e2311..63fdb4fb 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -30,10 +30,158 @@ #include #include #include +#include #include #include #include +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + bool mapBuilt = false; + + // We use a simple array instead of a map => constant time lookup + xcb_keycode_t keycodeMap[sf::Keyboard::KeyCount]; + + xcb_keycode_t getKeycode(xcb_key_symbols_t* keySymbols, xcb_keysym_t keysym) + { + // keycodes is actually a vector of keycodes + // Since we only care about the unmodified keycode, + // we simply return the first one + sf::priv::ScopedXcbPtr keycodes(xcb_key_symbols_get_keycode(keySymbols, keysym)); + + if (keycodes) + return *keycodes.get(); + + return 0; + } + + void buildMap() + { + // Open a connection with the X server + xcb_connection_t* connection = sf::priv::OpenConnection(); + + xcb_key_symbols_t* keySymbols = xcb_key_symbols_alloc(connection); + + if (!keySymbols) + { + sf::err() << "Failed to allocate key symbols" << std::endl; + return; + } + + keycodeMap[sf::Keyboard::A] = getKeycode(keySymbols, XK_A); + keycodeMap[sf::Keyboard::B] = getKeycode(keySymbols, XK_B); + keycodeMap[sf::Keyboard::C] = getKeycode(keySymbols, XK_C); + keycodeMap[sf::Keyboard::D] = getKeycode(keySymbols, XK_D); + keycodeMap[sf::Keyboard::E] = getKeycode(keySymbols, XK_E); + keycodeMap[sf::Keyboard::F] = getKeycode(keySymbols, XK_F); + keycodeMap[sf::Keyboard::G] = getKeycode(keySymbols, XK_G); + keycodeMap[sf::Keyboard::H] = getKeycode(keySymbols, XK_H); + keycodeMap[sf::Keyboard::I] = getKeycode(keySymbols, XK_I); + keycodeMap[sf::Keyboard::J] = getKeycode(keySymbols, XK_J); + keycodeMap[sf::Keyboard::K] = getKeycode(keySymbols, XK_K); + keycodeMap[sf::Keyboard::L] = getKeycode(keySymbols, XK_L); + keycodeMap[sf::Keyboard::M] = getKeycode(keySymbols, XK_M); + keycodeMap[sf::Keyboard::N] = getKeycode(keySymbols, XK_N); + keycodeMap[sf::Keyboard::O] = getKeycode(keySymbols, XK_O); + keycodeMap[sf::Keyboard::P] = getKeycode(keySymbols, XK_P); + keycodeMap[sf::Keyboard::Q] = getKeycode(keySymbols, XK_Q); + keycodeMap[sf::Keyboard::R] = getKeycode(keySymbols, XK_R); + keycodeMap[sf::Keyboard::S] = getKeycode(keySymbols, XK_S); + keycodeMap[sf::Keyboard::T] = getKeycode(keySymbols, XK_T); + keycodeMap[sf::Keyboard::U] = getKeycode(keySymbols, XK_U); + keycodeMap[sf::Keyboard::V] = getKeycode(keySymbols, XK_V); + keycodeMap[sf::Keyboard::W] = getKeycode(keySymbols, XK_W); + keycodeMap[sf::Keyboard::X] = getKeycode(keySymbols, XK_X); + keycodeMap[sf::Keyboard::Y] = getKeycode(keySymbols, XK_Y); + keycodeMap[sf::Keyboard::Z] = getKeycode(keySymbols, XK_Z); + keycodeMap[sf::Keyboard::Num0] = getKeycode(keySymbols, XK_0); + keycodeMap[sf::Keyboard::Num1] = getKeycode(keySymbols, XK_1); + keycodeMap[sf::Keyboard::Num2] = getKeycode(keySymbols, XK_2); + keycodeMap[sf::Keyboard::Num3] = getKeycode(keySymbols, XK_3); + keycodeMap[sf::Keyboard::Num4] = getKeycode(keySymbols, XK_4); + keycodeMap[sf::Keyboard::Num5] = getKeycode(keySymbols, XK_5); + keycodeMap[sf::Keyboard::Num6] = getKeycode(keySymbols, XK_6); + keycodeMap[sf::Keyboard::Num7] = getKeycode(keySymbols, XK_7); + keycodeMap[sf::Keyboard::Num8] = getKeycode(keySymbols, XK_8); + keycodeMap[sf::Keyboard::Num9] = getKeycode(keySymbols, XK_9); + keycodeMap[sf::Keyboard::Escape] = getKeycode(keySymbols, XK_Escape); + keycodeMap[sf::Keyboard::LControl] = getKeycode(keySymbols, XK_Control_L); + keycodeMap[sf::Keyboard::LShift] = getKeycode(keySymbols, XK_Shift_L); + keycodeMap[sf::Keyboard::LAlt] = getKeycode(keySymbols, XK_Alt_L); + keycodeMap[sf::Keyboard::LSystem] = getKeycode(keySymbols, XK_Super_L); + keycodeMap[sf::Keyboard::RControl] = getKeycode(keySymbols, XK_Control_R); + keycodeMap[sf::Keyboard::RShift] = getKeycode(keySymbols, XK_Shift_R); + keycodeMap[sf::Keyboard::RAlt] = getKeycode(keySymbols, XK_Alt_R); + keycodeMap[sf::Keyboard::RSystem] = getKeycode(keySymbols, XK_Super_R); + keycodeMap[sf::Keyboard::Menu] = getKeycode(keySymbols, XK_Menu); + keycodeMap[sf::Keyboard::LBracket] = getKeycode(keySymbols, XK_bracketleft); + keycodeMap[sf::Keyboard::RBracket] = getKeycode(keySymbols, XK_bracketright); + keycodeMap[sf::Keyboard::SemiColon] = getKeycode(keySymbols, XK_semicolon); + keycodeMap[sf::Keyboard::Comma] = getKeycode(keySymbols, XK_comma); + keycodeMap[sf::Keyboard::Period] = getKeycode(keySymbols, XK_period); + keycodeMap[sf::Keyboard::Quote] = getKeycode(keySymbols, XK_apostrophe); + keycodeMap[sf::Keyboard::Slash] = getKeycode(keySymbols, XK_slash); + keycodeMap[sf::Keyboard::BackSlash] = getKeycode(keySymbols, XK_backslash); + keycodeMap[sf::Keyboard::Tilde] = getKeycode(keySymbols, XK_grave); + keycodeMap[sf::Keyboard::Equal] = getKeycode(keySymbols, XK_equal); + keycodeMap[sf::Keyboard::Dash] = getKeycode(keySymbols, XK_minus); + keycodeMap[sf::Keyboard::Space] = getKeycode(keySymbols, XK_space); + keycodeMap[sf::Keyboard::Return] = getKeycode(keySymbols, XK_Return); + keycodeMap[sf::Keyboard::BackSpace] = getKeycode(keySymbols, XK_BackSpace); + keycodeMap[sf::Keyboard::Tab] = getKeycode(keySymbols, XK_Tab); + keycodeMap[sf::Keyboard::PageUp] = getKeycode(keySymbols, XK_Prior); + keycodeMap[sf::Keyboard::PageDown] = getKeycode(keySymbols, XK_Next); + keycodeMap[sf::Keyboard::End] = getKeycode(keySymbols, XK_End); + keycodeMap[sf::Keyboard::Home] = getKeycode(keySymbols, XK_Home); + keycodeMap[sf::Keyboard::Insert] = getKeycode(keySymbols, XK_Insert); + keycodeMap[sf::Keyboard::Delete] = getKeycode(keySymbols, XK_Delete); + keycodeMap[sf::Keyboard::Add] = getKeycode(keySymbols, XK_KP_Add); + keycodeMap[sf::Keyboard::Subtract] = getKeycode(keySymbols, XK_KP_Subtract); + keycodeMap[sf::Keyboard::Multiply] = getKeycode(keySymbols, XK_KP_Multiply); + keycodeMap[sf::Keyboard::Divide] = getKeycode(keySymbols, XK_KP_Divide); + keycodeMap[sf::Keyboard::Left] = getKeycode(keySymbols, XK_Left); + keycodeMap[sf::Keyboard::Right] = getKeycode(keySymbols, XK_Right); + keycodeMap[sf::Keyboard::Up] = getKeycode(keySymbols, XK_Up); + keycodeMap[sf::Keyboard::Down] = getKeycode(keySymbols, XK_Down); + keycodeMap[sf::Keyboard::Numpad0] = getKeycode(keySymbols, XK_KP_0); + keycodeMap[sf::Keyboard::Numpad1] = getKeycode(keySymbols, XK_KP_1); + keycodeMap[sf::Keyboard::Numpad2] = getKeycode(keySymbols, XK_KP_2); + keycodeMap[sf::Keyboard::Numpad3] = getKeycode(keySymbols, XK_KP_3); + keycodeMap[sf::Keyboard::Numpad4] = getKeycode(keySymbols, XK_KP_4); + keycodeMap[sf::Keyboard::Numpad5] = getKeycode(keySymbols, XK_KP_5); + keycodeMap[sf::Keyboard::Numpad6] = getKeycode(keySymbols, XK_KP_6); + keycodeMap[sf::Keyboard::Numpad7] = getKeycode(keySymbols, XK_KP_7); + keycodeMap[sf::Keyboard::Numpad8] = getKeycode(keySymbols, XK_KP_8); + keycodeMap[sf::Keyboard::Numpad9] = getKeycode(keySymbols, XK_KP_9); + keycodeMap[sf::Keyboard::F1] = getKeycode(keySymbols, XK_F1); + keycodeMap[sf::Keyboard::F2] = getKeycode(keySymbols, XK_F2); + keycodeMap[sf::Keyboard::F3] = getKeycode(keySymbols, XK_F3); + keycodeMap[sf::Keyboard::F4] = getKeycode(keySymbols, XK_F4); + keycodeMap[sf::Keyboard::F5] = getKeycode(keySymbols, XK_F5); + keycodeMap[sf::Keyboard::F6] = getKeycode(keySymbols, XK_F6); + keycodeMap[sf::Keyboard::F7] = getKeycode(keySymbols, XK_F7); + keycodeMap[sf::Keyboard::F8] = getKeycode(keySymbols, XK_F8); + keycodeMap[sf::Keyboard::F9] = getKeycode(keySymbols, XK_F9); + keycodeMap[sf::Keyboard::F10] = getKeycode(keySymbols, XK_F10); + keycodeMap[sf::Keyboard::F11] = getKeycode(keySymbols, XK_F11); + keycodeMap[sf::Keyboard::F12] = getKeycode(keySymbols, XK_F12); + keycodeMap[sf::Keyboard::F13] = getKeycode(keySymbols, XK_F13); + keycodeMap[sf::Keyboard::F14] = getKeycode(keySymbols, XK_F14); + keycodeMap[sf::Keyboard::F15] = getKeycode(keySymbols, XK_F15); + keycodeMap[sf::Keyboard::Pause] = getKeycode(keySymbols, XK_Pause); + + xcb_key_symbols_free(keySymbols); + + // Close the connection with the X server + sf::priv::CloseConnection(connection); + + mapBuilt = true; + } +} + namespace sf { @@ -42,153 +190,42 @@ namespace priv //////////////////////////////////////////////////////////// bool InputImpl::isKeyPressed(Keyboard::Key key) { - // Get the corresponding X11 keysym - KeySym keysym = 0; - switch (key) - { - case Keyboard::A: keysym = XK_A; break; - case Keyboard::B: keysym = XK_B; break; - case Keyboard::C: keysym = XK_C; break; - case Keyboard::D: keysym = XK_D; break; - case Keyboard::E: keysym = XK_E; break; - case Keyboard::F: keysym = XK_F; break; - case Keyboard::G: keysym = XK_G; break; - case Keyboard::H: keysym = XK_H; break; - case Keyboard::I: keysym = XK_I; break; - case Keyboard::J: keysym = XK_J; break; - case Keyboard::K: keysym = XK_K; break; - case Keyboard::L: keysym = XK_L; break; - case Keyboard::M: keysym = XK_M; break; - case Keyboard::N: keysym = XK_N; break; - case Keyboard::O: keysym = XK_O; break; - case Keyboard::P: keysym = XK_P; break; - case Keyboard::Q: keysym = XK_Q; break; - case Keyboard::R: keysym = XK_R; break; - case Keyboard::S: keysym = XK_S; break; - case Keyboard::T: keysym = XK_T; break; - case Keyboard::U: keysym = XK_U; break; - case Keyboard::V: keysym = XK_V; break; - case Keyboard::W: keysym = XK_W; break; - case Keyboard::X: keysym = XK_X; break; - case Keyboard::Y: keysym = XK_Y; break; - case Keyboard::Z: keysym = XK_Z; break; - case Keyboard::Num0: keysym = XK_0; break; - case Keyboard::Num1: keysym = XK_1; break; - case Keyboard::Num2: keysym = XK_2; break; - case Keyboard::Num3: keysym = XK_3; break; - case Keyboard::Num4: keysym = XK_4; break; - case Keyboard::Num5: keysym = XK_5; break; - case Keyboard::Num6: keysym = XK_6; break; - case Keyboard::Num7: keysym = XK_7; break; - case Keyboard::Num8: keysym = XK_8; break; - case Keyboard::Num9: keysym = XK_9; break; - case Keyboard::Escape: keysym = XK_Escape; break; - case Keyboard::LControl: keysym = XK_Control_L; break; - case Keyboard::LShift: keysym = XK_Shift_L; break; - case Keyboard::LAlt: keysym = XK_Alt_L; break; - case Keyboard::LSystem: keysym = XK_Super_L; break; - case Keyboard::RControl: keysym = XK_Control_R; break; - case Keyboard::RShift: keysym = XK_Shift_R; break; - case Keyboard::RAlt: keysym = XK_Alt_R; break; - case Keyboard::RSystem: keysym = XK_Super_R; break; - case Keyboard::Menu: keysym = XK_Menu; break; - case Keyboard::LBracket: keysym = XK_bracketleft; break; - case Keyboard::RBracket: keysym = XK_bracketright; break; - case Keyboard::SemiColon: keysym = XK_semicolon; break; - case Keyboard::Comma: keysym = XK_comma; break; - case Keyboard::Period: keysym = XK_period; break; - case Keyboard::Quote: keysym = XK_dead_acute; break; - case Keyboard::Slash: keysym = XK_slash; break; - case Keyboard::BackSlash: keysym = XK_backslash; break; - case Keyboard::Tilde: keysym = XK_dead_grave; break; - case Keyboard::Equal: keysym = XK_equal; break; - case Keyboard::Dash: keysym = XK_minus; break; - case Keyboard::Space: keysym = XK_space; break; - case Keyboard::Return: keysym = XK_Return; break; - case Keyboard::BackSpace: keysym = XK_BackSpace; break; - case Keyboard::Tab: keysym = XK_Tab; break; - case Keyboard::PageUp: keysym = XK_Prior; break; - case Keyboard::PageDown: keysym = XK_Next; break; - case Keyboard::End: keysym = XK_End; break; - case Keyboard::Home: keysym = XK_Home; break; - case Keyboard::Insert: keysym = XK_Insert; break; - case Keyboard::Delete: keysym = XK_Delete; break; - case Keyboard::Add: keysym = XK_KP_Add; break; - case Keyboard::Subtract: keysym = XK_KP_Subtract; break; - case Keyboard::Multiply: keysym = XK_KP_Multiply; break; - case Keyboard::Divide: keysym = XK_KP_Divide; break; - case Keyboard::Left: keysym = XK_Left; break; - case Keyboard::Right: keysym = XK_Right; break; - case Keyboard::Up: keysym = XK_Up; break; - case Keyboard::Down: keysym = XK_Down; break; - case Keyboard::Numpad0: keysym = XK_KP_0; break; - case Keyboard::Numpad1: keysym = XK_KP_1; break; - case Keyboard::Numpad2: keysym = XK_KP_2; break; - case Keyboard::Numpad3: keysym = XK_KP_3; break; - case Keyboard::Numpad4: keysym = XK_KP_4; break; - case Keyboard::Numpad5: keysym = XK_KP_5; break; - case Keyboard::Numpad6: keysym = XK_KP_6; break; - case Keyboard::Numpad7: keysym = XK_KP_7; break; - case Keyboard::Numpad8: keysym = XK_KP_8; break; - case Keyboard::Numpad9: keysym = XK_KP_9; break; - case Keyboard::F1: keysym = XK_F1; break; - case Keyboard::F2: keysym = XK_F2; break; - case Keyboard::F3: keysym = XK_F3; break; - case Keyboard::F4: keysym = XK_F4; break; - case Keyboard::F5: keysym = XK_F5; break; - case Keyboard::F6: keysym = XK_F6; break; - case Keyboard::F7: keysym = XK_F7; break; - case Keyboard::F8: keysym = XK_F8; break; - case Keyboard::F9: keysym = XK_F9; break; - case Keyboard::F10: keysym = XK_F10; break; - case Keyboard::F11: keysym = XK_F11; break; - case Keyboard::F12: keysym = XK_F12; break; - case Keyboard::F13: keysym = XK_F13; break; - case Keyboard::F14: keysym = XK_F14; break; - case Keyboard::F15: keysym = XK_F15; break; - case Keyboard::Pause: keysym = XK_Pause; break; - default: keysym = 0; break; - } + if (!mapBuilt) + buildMap(); - // Open a connection with the X server - Display* display = OpenDisplay(); - xcb_connection_t* connection = XGetXCBConnection(display); + // Sanity checks + if (key < 0 || key >= sf::Keyboard::KeyCount) + return false; // Convert to keycode - KeyCode keycode = XKeysymToKeycode(display, keysym); - if (keycode != 0) + xcb_keycode_t keycode = keycodeMap[key]; + + ScopedXcbPtr error(NULL); + + // Open a connection with the X server + xcb_connection_t* connection = OpenConnection(); + + // Get the whole keyboard state + ScopedXcbPtr keymap( + xcb_query_keymap_reply( + connection, + xcb_query_keymap(connection), + &error + ) + ); + + // Close the connection with the X server + CloseConnection(connection); + + if (error) { - ScopedXcbPtr error(NULL); - - // Get the whole keyboard state - ScopedXcbPtr keymap( - xcb_query_keymap_reply( - connection, - xcb_query_keymap(connection), - &error - ) - ); - - // Close the connection with the X server - CloseDisplay(display); - - if (error) - { - err() << "Failed to query keymap" << std::endl; - - return false; - } - - // Check our keycode - return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; - } - else - { - // Close the connection with the X server - CloseDisplay(display); + err() << "Failed to query keymap" << std::endl; return false; } + + // Check our keycode + return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; } diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index b05e88dd..2af4deeb 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -308,6 +308,7 @@ WindowImplX11::WindowImplX11(WindowHandle handle) : m_window (0), m_inputMethod (NULL), m_inputContext (NULL), +m_keySymbols (NULL), m_isExternal (true), m_atomWmProtocols(0), m_atomClose (0), @@ -379,6 +380,7 @@ WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long m_window (0), m_inputMethod (NULL), m_inputContext (NULL), +m_keySymbols (NULL), m_isExternal (false), m_atomWmProtocols(0), m_atomClose (0), @@ -541,6 +543,10 @@ WindowImplX11::~WindowImplX11() xcb_flush(m_connection); } + // Free key symbols + if (m_keySymbols) + xcb_key_symbols_free(m_keySymbols); + // Close the input method if (m_inputMethod) XCloseIM(m_inputMethod); @@ -1619,6 +1625,12 @@ void WindowImplX11::initialize() if (!m_inputContext) err() << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl; + // Allocate our key symbols + m_keySymbols = xcb_key_symbols_alloc(m_connection); + + if (!m_keySymbols) + err() << "Failed to allocate key symbols" << std::endl; + // Show the window setVisible(true); @@ -1857,20 +1869,8 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) xcb_key_press_event_t* e = reinterpret_cast(windowEvent); // Get the keysym of the key that has been pressed - static XComposeStatus keyboard; - char buffer[32]; - KeySym symbol; - - // There is no xcb equivalent of XLookupString, so the xcb event - // has to be converted to an XEvent - XEvent fake_event; - fake_event.type = KeyPress; - fake_event.xany.display = m_display; - fake_event.xany.window = e->event; - fake_event.xkey.state = e->state; - fake_event.xkey.keycode = e->detail; - - XLookupString(&fake_event.xkey, buffer, sizeof(buffer), &symbol, &keyboard); + // We don't pass e->state as the last parameter because we want the unmodified keysym + xcb_keysym_t symbol = xcb_key_press_lookup_keysym(m_keySymbols, e, 0); // Fill the event parameters // TODO: if modifiers are wrong, use XGetModifierMapping to retrieve the actual modifiers mapping @@ -1883,8 +1883,15 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) event.key.system = e->state & XCB_MOD_MASK_4; pushEvent(event); + XEvent fakeEvent; + fakeEvent.type = KeyPress; + fakeEvent.xany.display = m_display; + fakeEvent.xany.window = e->event; + fakeEvent.xkey.state = e->state; + fakeEvent.xkey.keycode = e->detail; + // Generate a TextEntered event - if (!XFilterEvent(&fake_event, None)) + if (!XFilterEvent(&fakeEvent, None)) { #ifdef X_HAVE_UTF8_STRING if (m_inputContext) @@ -1894,7 +1901,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) int length = Xutf8LookupString( m_inputContext, - &fake_event.xkey, + &fakeEvent.xkey, reinterpret_cast(keyBuffer), sizeof(keyBuffer), NULL, @@ -1919,7 +1926,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) { static XComposeStatus status; char keyBuffer[16]; - if (XLookupString(&fake_event.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status)) + if (XLookupString(&fakeEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status)) { Event textEvent; textEvent.type = Event::TextEntered; @@ -1941,16 +1948,8 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) xcb_key_release_event_t* e = reinterpret_cast(windowEvent); // Get the keysym of the key that has been pressed - char buffer[32]; - KeySym symbol; - - // There is no xcb equivalent of XLookupString, so the xcb event - // has to be converted to an XEvent - XKeyEvent fake_event; - fake_event.display = m_display; - fake_event.state = e->state; - fake_event.keycode = e->detail; - XLookupString(&fake_event, buffer, 32, &symbol, NULL); + // We don't pass e->state as the last parameter because we want the unmodified keysym + xcb_keysym_t symbol = xcb_key_release_lookup_keysym(m_keySymbols, e, 0); // Fill the event parameters Event event; @@ -2253,9 +2252,9 @@ Keyboard::Key WindowImplX11::keysymToSF(xcb_keysym_t symbol) case XK_bracketright: return Keyboard::RBracket; case XK_comma: return Keyboard::Comma; case XK_period: return Keyboard::Period; - case XK_dead_acute: return Keyboard::Quote; + case XK_apostrophe: return Keyboard::Quote; case XK_backslash: return Keyboard::BackSlash; - case XK_dead_grave: return Keyboard::Tilde; + case XK_grave: return Keyboard::Tilde; case XK_space: return Keyboard::Space; case XK_Return: return Keyboard::Return; case XK_KP_Enter: return Keyboard::Return; diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 5bd596dd..65427907 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -278,6 +279,7 @@ private: 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 + xcb_key_symbols_t* m_keySymbols; ///< Symbols used to look up keysyms 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