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.
This commit is contained in:
Stefan Schindler 2015-01-09 12:52:59 +01:00 committed by Lukas Dürrenberger
parent 20f213bfac
commit c08a56bf9f
2 changed files with 141 additions and 127 deletions

View File

@ -35,7 +35,6 @@
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
#include <xcb/xcb_image.h> #include <xcb/xcb_image.h>
#include <xcb/xcb_util.h> #include <xcb/xcb_util.h>
#include <xcb/randr.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
@ -63,9 +62,11 @@ namespace
{ {
sf::priv::WindowImplX11* fullscreenWindow = NULL; sf::priv::WindowImplX11* fullscreenWindow = NULL;
std::vector<sf::priv::WindowImplX11*> allWindows; std::vector<sf::priv::WindowImplX11*> allWindows;
unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | unsigned long eventMask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS |
PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION |
EnterWindowMask | LeaveWindowMask; 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 // Find the name of the current executable
std::string findExecutableName() std::string findExecutableName()
@ -103,7 +104,8 @@ m_oldVideoMode(-1),
m_hiddenCursor(0), m_hiddenCursor(0),
m_keyRepeat (true), m_keyRepeat (true),
m_previousSize(-1, -1), m_previousSize(-1, -1),
m_useSizeHints(false) m_useSizeHints(false),
m_fullscreen (false)
{ {
// Open a connection with the X server // Open a connection with the X server
m_display = OpenDisplay(); m_display = OpenDisplay();
@ -150,7 +152,8 @@ m_oldVideoMode(-1),
m_hiddenCursor(0), m_hiddenCursor(0),
m_keyRepeat (true), m_keyRepeat (true),
m_previousSize(-1, -1), m_previousSize(-1, -1),
m_useSizeHints(false) m_useSizeHints(false),
m_fullscreen ((style & Style::Fullscreen) != 0)
{ {
// Open a connection with the X server // Open a connection with the X server
m_display = OpenDisplay(); m_display = OpenDisplay();
@ -166,23 +169,18 @@ m_useSizeHints(false)
XSetEventQueueOwner(m_display, XCBOwnsEventQueue); XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
// Compute position and size // Compute position and size
bool fullscreen = (style & Style::Fullscreen) != 0; int left = m_fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2;
int left = fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2; int top = m_fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2;
int top = fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2;
int width = mode.width; int width = mode.width;
int height = mode.height; int height = mode.height;
// Switch to fullscreen if necessary
if (fullscreen)
switchToFullscreen(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);
// Define the window attributes // Define the window attributes
xcb_colormap_t colormap = xcb_generate_id(m_connection); xcb_colormap_t colormap = xcb_generate_id(m_connection);
xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid); xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid);
const uint32_t value_list[] = {fullscreen, static_cast<uint32_t>(eventMask), colormap}; const uint32_t value_list[] = {static_cast<uint32_t>(eventMask), colormap};
// Create the window // Create the window
m_window = xcb_generate_id(m_connection); m_window = xcb_generate_id(m_connection);
@ -199,7 +197,7 @@ m_useSizeHints(false)
0, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_INPUT_OUTPUT,
visualInfo.visualid, visualInfo.visualid,
XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP, XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
value_list value_list
) )
)); ));
@ -214,7 +212,7 @@ m_useSizeHints(false)
setTitle(title); setTitle(title);
// Set the window's style (tell the window manager to change our window's decorations and functions according to the requested style) // 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"; static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS";
ScopedXcbPtr<xcb_intern_atom_reply_t> hintsAtomReply(xcb_intern_atom_reply( ScopedXcbPtr<xcb_intern_atom_reply_t> hintsAtomReply(xcb_intern_atom_reply(
@ -309,30 +307,9 @@ m_useSizeHints(false)
// Do some common initializations // Do some common initializations
initialize(); initialize();
// In fullscreen mode, we must grab keyboard and mouse inputs // Switch to fullscreen.
if (fullscreen) if (m_fullscreen)
{ switchToFullscreen();
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
);
}
} }
@ -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<xcb_grab_pointer_reply_t> 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<int>(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() 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 // Create atom for _NET_WM_STATE.
static const std::string RANDR = "RANDR"; static const std::string netWmState = "_NET_WM_STATE";
ScopedXcbPtr<xcb_query_extension_reply_t> randr_ext(xcb_query_extension_reply( ScopedXcbPtr<xcb_intern_atom_reply_t> stateReply(xcb_intern_atom_reply(
m_connection, m_connection,
xcb_query_extension( xcb_intern_atom(
m_connection, m_connection,
RANDR.size(), 0,
RANDR.c_str() netWmState.size(),
), netWmState.c_str()
NULL ),
0
)); ));
if (randr_ext->present) // Create atom for _NET_WM_STATE_FULLSCREEN.
{ static const std::string netWmStateFullscreen = "_NET_WM_STATE_FULLSCREEN";
// Get the current configuration ScopedXcbPtr<xcb_intern_atom_reply_t> fullscreenReply(xcb_intern_atom_reply(
ScopedXcbPtr<xcb_generic_error_t> error(NULL); m_connection,
ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply( xcb_intern_atom(
m_connection, m_connection,
xcb_randr_get_screen_info( 0,
m_connection, netWmStateFullscreen.size(),
m_screen->root netWmStateFullscreen.c_str()
), ),
&error 0
)); ));
if (!error) // Set fullscreen property.
{ ScopedXcbPtr<xcb_generic_error_t> fullscreenError(xcb_request_check(
// Save the current video mode before we switch to fullscreen m_connection,
m_oldVideoMode = config->sizeID; 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 if (fullscreenError)
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 sf::err() << "Setting fullscreen failed." << std::endl;
err() << "Fullscreen is not supported, switching to window mode" << std::endl; return;
} }
// Create atom for _NET_WM_BYPASS_COMPOSITOR.
static const std::string netWmStateBypassCompositor = "_NET_WM_BYPASS_COMPOSITOR";
ScopedXcbPtr<xcb_intern_atom_reply_t> compReply(xcb_intern_atom_reply(
m_connection,
xcb_intern_atom(
m_connection,
0,
netWmStateBypassCompositor.size(),
netWmStateBypassCompositor.c_str()
),
0
));
// Disable compositor.
ScopedXcbPtr<xcb_generic_error_t> 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) // Restore the previous video mode (in case we were running in fullscreen)
if (fullscreenWindow == this) if (fullscreenWindow == this)
{ {
// Get current screen info
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)
{
// 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 // Reset the fullscreen window
fullscreenWindow = NULL; fullscreenWindow = NULL;
@ -1001,12 +1000,20 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
hints.flags &= ~XUrgencyHint; hints.flags &= ~XUrgencyHint;
xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints);
// Grab pointer if necessary.
if (m_fullscreen)
setPointerGrabbed(true);
break; break;
} }
// Lost focus event // Lost focus event
case XCB_FOCUS_OUT: case XCB_FOCUS_OUT:
{ {
// Ungrab pointer if necessary.
if (m_fullscreen)
setPointerGrabbed(false);
// Update the input context // Update the input context
if (m_inputContext) if (m_inputContext)
XUnsetICFocus(m_inputContext); XUnsetICFocus(m_inputContext);

View File

@ -182,10 +182,16 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Switch to fullscreen mode /// \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 /// \brief Do some common initializations after the window has been created
@ -241,6 +247,7 @@ private:
bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? 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) 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_useSizeHints; ///< Is the size of the window fixed with size hints?
bool m_fullscreen; ///< Is window in fullscreen?
}; };
} // namespace priv } // namespace priv