From 623f63a48aaff72062ac7c4adc06f96506ce65c9 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Sat, 14 Mar 2015 06:17:41 +0100 Subject: [PATCH] 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