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).

This commit is contained in:
binary1248 2015-03-14 06:17:41 +01:00 committed by Lukas Dürrenberger
parent 3faf2a93d6
commit 623f63a48a
2 changed files with 303 additions and 164 deletions

View File

@ -48,7 +48,9 @@ std::vector<VideoMode> VideoModeImpl::getFullscreenModes()
// Retrieve the default screen // Retrieve the default screen
xcb_screen_t* screen = XCBDefaultScreen(connection); xcb_screen_t* screen = XCBDefaultScreen(connection);
// Check if the XRandR extension is present ScopedXcbPtr<xcb_generic_error_t> error(NULL);
// Check if the RandR extension is present
static const std::string RANDR = "RANDR"; static const std::string RANDR = "RANDR";
ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply( ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply(
connection, connection,
@ -56,56 +58,86 @@ std::vector<VideoMode> VideoModeImpl::getFullscreenModes()
connection, connection,
RANDR.size(), RANDR.size(),
RANDR.c_str() RANDR.c_str()
), ),
NULL &error
)); ));
if (randr_ext->present) if (error || !randr_ext->present)
{ {
// Get the current configuration // Randr extension is not supported: we cannot get the video modes
ScopedXcbPtr<xcb_generic_error_t> error(NULL); err() << "Failed to use the RandR extension while trying to get the supported video modes" << std::endl;
ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply(
// Close the connection with the X server
CloseConnection(connection);
return modes;
}
// Load RandR and check its version
ScopedXcbPtr<xcb_randr_query_version_reply_t> randrVersion(xcb_randr_query_version_reply(
connection,
xcb_randr_query_version(
connection, connection,
xcb_randr_get_screen_info( 1,
connection, 1
screen->root ),
), &error
&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<xcb_randr_get_screen_info_reply_t> 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 for (int j = 0; j < config->nSizes; ++j)
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 // Convert to VideoMode
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); VideoMode mode(sizes[j].width, sizes[j].height, iter.data->depth);
// 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);
// Add it only if it is not already in the array if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
if (std::find(modes.begin(), modes.end(), mode) == modes.end()) config->rotation == XCB_RANDR_ROTATION_ROTATE_270)
modes.push_back(mode); 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 // Close the connection with the X server
@ -126,7 +158,9 @@ VideoMode VideoModeImpl::getDesktopMode()
// Retrieve the default screen // Retrieve the default screen
xcb_screen_t* screen = XCBDefaultScreen(connection); xcb_screen_t* screen = XCBDefaultScreen(connection);
// Check if the XRandR extension is present ScopedXcbPtr<xcb_generic_error_t> error(NULL);
// Check if the RandR extension is present
static const std::string RANDR = "RANDR"; static const std::string RANDR = "RANDR";
ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply( ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply(
connection, connection,
@ -134,44 +168,80 @@ VideoMode VideoModeImpl::getDesktopMode()
connection, connection,
RANDR.size(), RANDR.size(),
RANDR.c_str() RANDR.c_str()
), ),
NULL &error
)); ));
if (randr_ext->present) if (error || !randr_ext->present)
{ {
// Get the current configuration // Randr extension is not supported: we cannot get the video modes
ScopedXcbPtr<xcb_generic_error_t> error(NULL); err() << "Failed to use the RandR extension while trying to get the desktop video mode" << std::endl;
ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply(
// Close the connection with the X server
CloseConnection(connection);
return desktopMode;
}
// Load RandR and check its version
ScopedXcbPtr<xcb_randr_query_version_reply_t> randrVersion(xcb_randr_query_version_reply(
connection,
xcb_randr_query_version(
connection, connection,
xcb_randr_get_screen_info( 1,
connection, 1
screen->root ),
), &error
&error ));
));
if (!error) if (error)
{ {
// Get the current video mode err() << "Failed to load the RandR extension while trying to get the desktop video mode" << std::endl;
xcb_randr_mode_t currentMode = config->sizeID;
// Get the available screen sizes // Close the connection with the X server
int nbSizes = xcb_randr_get_screen_info_sizes_length(config.get()); CloseConnection(connection);
xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get());
if (sizes && (nbSizes > 0)) return desktopMode;
desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth); }
}
else // Get the current configuration
{ ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply(
// Failed to get the screen configuration connection,
err() << "Failed to retrieve the screen configuration while trying to get the desktop video modes" << std::endl; 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 else
{ {
// XRandr extension is not supported: we cannot get the video modes err() << "Failed to retrieve any screen sizes while trying to get the desktop video mode" << std::endl;
err() << "Failed to use the XRandR extension while trying to get the desktop video modes" << std::endl;
} }
// Close the connection with the X server // Close the connection with the X server

View File

@ -91,6 +91,14 @@ namespace
// Check if Extended Window Manager Hints are supported // Check if Extended Window Manager Hints are supported
bool ewmhSupported() bool ewmhSupported()
{ {
static bool checked = false;
static bool ewmhSupported = false;
if (checked)
return ewmhSupported;
checked = true;
xcb_connection_t* connection = sf::priv::OpenConnection(); xcb_connection_t* connection = sf::priv::OpenConnection();
static const std::string NET_SUPPORTING_WM_CHECK = "_NET_SUPPORTING_WM_CHECK"; static const std::string NET_SUPPORTING_WM_CHECK = "_NET_SUPPORTING_WM_CHECK";
@ -98,7 +106,7 @@ namespace
connection, connection,
xcb_intern_atom( xcb_intern_atom(
connection, connection,
0, 1,
NET_SUPPORTING_WM_CHECK.size(), NET_SUPPORTING_WM_CHECK.size(),
NET_SUPPORTING_WM_CHECK.c_str() NET_SUPPORTING_WM_CHECK.c_str()
), ),
@ -116,7 +124,7 @@ namespace
connection, connection,
xcb_intern_atom( xcb_intern_atom(
connection, connection,
0, 1,
NET_SUPPORTED.size(), NET_SUPPORTED.size(),
NET_SUPPORTED.c_str() NET_SUPPORTED.c_str()
), ),
@ -145,7 +153,10 @@ namespace
&error &error
)); ));
if (!rootSupportingWindow) if (!rootSupportingWindow ||
(rootSupportingWindow->length != 1) ||
(rootSupportingWindow->format != 32) ||
(rootSupportingWindow->type != XCB_ATOM_WINDOW))
{ {
sf::priv::CloseConnection(connection); sf::priv::CloseConnection(connection);
return false; return false;
@ -167,7 +178,10 @@ namespace
&error &error
)); ));
if (!childSupportingWindow) if (!childSupportingWindow ||
(childSupportingWindow->length != 1) ||
(childSupportingWindow->format != 32) ||
(childSupportingWindow->type != XCB_ATOM_WINDOW))
{ {
sf::priv::CloseConnection(connection); sf::priv::CloseConnection(connection);
return false; return false;
@ -183,6 +197,7 @@ namespace
} }
sf::priv::CloseConnection(connection); sf::priv::CloseConnection(connection);
ewmhSupported = true;
return true; return true;
} }
} }
@ -218,6 +233,9 @@ m_fullscreen (false)
return; return;
} }
// Make sure to check for EWMH support before we do anything
ewmhSupported();
m_screen = XCBDefaultScreen(m_connection); m_screen = XCBDefaultScreen(m_connection);
XSetEventQueueOwner(m_display, XCBOwnsEventQueue); XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
@ -268,6 +286,9 @@ m_fullscreen ((style & Style::Fullscreen) != 0)
return; return;
} }
// Make sure to check for EWMH support before we do anything
ewmhSupported();
m_screen = XCBDefaultScreen(m_connection); m_screen = XCBDefaultScreen(m_connection);
XSetEventQueueOwner(m_display, XCBOwnsEventQueue); XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
@ -277,10 +298,6 @@ m_fullscreen ((style & Style::Fullscreen) != 0)
int width = mode.width; int width = mode.width;
int height = mode.height; int height = mode.height;
// Set fullscreen video mode if necessary
if (m_fullscreen)
setVideoMode(mode);
// Choose the visual according to the context settings // Choose the visual according to the context settings
XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings); XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings);
@ -420,9 +437,13 @@ m_fullscreen ((style & Style::Fullscreen) != 0)
// Do some common initializations // Do some common initializations
initialize(); initialize();
// Switch to fullscreen if necessary // Set fullscreen video mode and switch to fullscreen if necessary
if (m_fullscreen) if (m_fullscreen)
{
setPosition(Vector2i(0, 0));
setVideoMode(mode);
switchToFullscreen(); switchToFullscreen();
}
} }
@ -870,7 +891,9 @@ void WindowImplX11::setVideoMode(const VideoMode& mode)
if (mode == VideoMode::getDesktopMode()) if (mode == VideoMode::getDesktopMode())
return; return;
// Check if the XRandR extension is present ScopedXcbPtr<xcb_generic_error_t> error(NULL);
// Check if the RandR extension is present
static const std::string RANDR = "RANDR"; static const std::string RANDR = "RANDR";
ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply( ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply(
m_connection, m_connection,
@ -879,66 +902,97 @@ void WindowImplX11::setVideoMode(const VideoMode& mode)
RANDR.size(), RANDR.size(),
RANDR.c_str() RANDR.c_str()
), ),
NULL &error
)); ));
if (randr_ext->present) if (error || !randr_ext->present)
{ {
// Get the current configuration // RandR extension is not supported: we cannot use fullscreen mode
ScopedXcbPtr<xcb_generic_error_t> error(NULL);
ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> 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<int>(mode.width)) &&
(sizes[i].height == static_cast<int>(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; err() << "Fullscreen is not supported, switching to window mode" << std::endl;
return;
} }
// Load RandR and check its version
ScopedXcbPtr<xcb_randr_query_version_reply_t> 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<xcb_randr_get_screen_info_reply_t> 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<int>(mode.width)) &&
(sizes[i].height == static_cast<int>(mode.height)))
{
// Switch to fullscreen mode
ScopedXcbPtr<xcb_randr_set_screen_config_reply_t> 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) if (!error)
{ {
// Reset the video mode // Reset the video mode
xcb_randr_set_screen_config( ScopedXcbPtr<xcb_randr_set_screen_config_reply_t> setScreenConfig(xcb_randr_set_screen_config_reply(
m_connection, m_connection,
m_screen->root, xcb_randr_set_screen_config(
CurrentTime, m_connection,
config->config_timestamp, m_screen->root,
m_oldVideoMode, CurrentTime,
config->rotation, config->config_timestamp,
config->rate m_oldVideoMode,
); config->rotation,
config->rate
),
&error
));
if (error)
err() << "Failed to reset old screen configuration" << std::endl;
} }
// Reset the fullscreen window // Reset the fullscreen window
@ -1016,7 +1077,7 @@ void WindowImplX11::switchToFullscreen()
// Not being able to bypass the compositor is not a fatal error // Not being able to bypass the compositor is not a fatal error
if (compositorError) 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. // Create atom for _NET_ACTIVE_WINDOW.
static const std::string netActiveWindow = "_NET_ACTIVE_WINDOW"; static const std::string netActiveWindow = "_NET_ACTIVE_WINDOW";
@ -1037,15 +1098,15 @@ void WindowImplX11::switchToFullscreen()
return; 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; activeWindowEvent.response_type = XCB_CLIENT_MESSAGE;
event.window = m_window; activeWindowEvent.window = m_window;
event.format = 32; activeWindowEvent.format = 32;
event.sequence = 0; activeWindowEvent.type = activeWindowReply->atom;
event.type = activeWindowReply->atom; activeWindowEvent.data.data32[0] = 1;
event.data.data32[0] = 1; activeWindowEvent.data.data32[1] = XCB_CURRENT_TIME;
event.data.data32[1] = XCB_CURRENT_TIME;
ScopedXcbPtr<xcb_generic_error_t> activeWindowError(xcb_request_check( ScopedXcbPtr<xcb_generic_error_t> activeWindowError(xcb_request_check(
m_connection, m_connection,
@ -1054,7 +1115,7 @@ void WindowImplX11::switchToFullscreen()
0, 0,
XCBDefaultRootWindow(m_connection), XCBDefaultRootWindow(m_connection),
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
reinterpret_cast<char*>(&event) reinterpret_cast<char*>(&activeWindowEvent)
) )
)); ));
@ -1102,15 +1163,17 @@ void WindowImplX11::switchToFullscreen()
return; return;
} }
event.response_type = XCB_CLIENT_MESSAGE; xcb_client_message_event_t wmStateEvent;
event.window = m_window; std::memset(&wmStateEvent, 0, sizeof(wmStateEvent));
event.format = 32;
event.sequence = 0; wmStateEvent.response_type = XCB_CLIENT_MESSAGE;
event.type = stateReply->atom; wmStateEvent.window = m_window;
event.data.data32[0] = 1; // _NET_WM_STATE_ADD wmStateEvent.format = 32;
event.data.data32[1] = fullscreenReply->atom; wmStateEvent.type = stateReply->atom;
event.data.data32[2] = 0; wmStateEvent.data.data32[0] = 1; // _NET_WM_STATE_ADD
event.data.data32[3] = 1; wmStateEvent.data.data32[1] = fullscreenReply->atom;
wmStateEvent.data.data32[2] = 0;
wmStateEvent.data.data32[3] = 1;
ScopedXcbPtr<xcb_generic_error_t> wmStateError(xcb_request_check( ScopedXcbPtr<xcb_generic_error_t> wmStateError(xcb_request_check(
m_connection, m_connection,
@ -1119,7 +1182,7 @@ void WindowImplX11::switchToFullscreen()
0, 0,
XCBDefaultRootWindow(m_connection), XCBDefaultRootWindow(m_connection),
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
reinterpret_cast<char*>(&event) reinterpret_cast<char*>(&wmStateEvent)
) )
)); ));
@ -1291,6 +1354,12 @@ void WindowImplX11::initialize()
// Show the window // Show the window
xcb_map_window(m_connection, m_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); xcb_flush(m_connection);
// Create the hidden cursor // Create the hidden cursor