From 46ce05cd9c336020e911a1f6a3486132dc31b679 Mon Sep 17 00:00:00 2001 From: Bloodsword Date: Wed, 1 Nov 2017 19:34:45 +0100 Subject: [PATCH] Use XRRSetCrtcConfig instead of XRRSetScreenConfig, in order to fix issue #1226. It should also fix issue #1224. --- src/SFML/Window/Unix/WindowImplX11.cpp | 313 ++++++++++++++++++++----- src/SFML/Window/Unix/WindowImplX11.hpp | 34 +++ 2 files changed, 292 insertions(+), 55 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index ee5bf3561..da6975052 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -174,7 +174,7 @@ namespace if (result != Success || actualType != XA_WINDOW || numItems != 1) { - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -206,7 +206,7 @@ namespace if (result != Success || actualType != XA_WINDOW || numItems != 1) { - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -270,7 +270,7 @@ namespace windowManagerName = sf::String::fromUtf8(begin, end); } - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -487,6 +487,7 @@ m_inputMethod (NULL), m_inputContext (NULL), m_isExternal (true), m_oldVideoMode (0), +m_oldRRCrtc (0), m_hiddenCursor (0), m_lastCursor (None), m_keyRepeat (true), @@ -535,6 +536,7 @@ m_inputMethod (NULL), m_inputContext (NULL), m_isExternal (false), m_oldVideoMode (0), +m_oldRRCrtc (0), m_hiddenCursor (0), m_lastCursor (None), m_keyRepeat (true), @@ -556,8 +558,17 @@ m_lastInputTime (0) m_screen = DefaultScreen(m_display); // Compute position and size - int left = m_fullscreen ? 0 : (DisplayWidth(m_display, m_screen) - mode.width) / 2; - int top = m_fullscreen ? 0 : (DisplayHeight(m_display, m_screen) - mode.height) / 2; + Vector2i windowPosition; + if(m_fullscreen) + { + windowPosition = getPrimaryMonitorPosition(); + } + else + { + windowPosition.x = (DisplayWidth(m_display, m_screen) - mode.width) / 2; + windowPosition.y = (DisplayWidth(m_display, m_screen) - mode.height) / 2; + } + int width = mode.width; int height = mode.height; @@ -572,7 +583,7 @@ m_lastInputTime (0) m_window = XCreateWindow(m_display, DefaultRootWindow(m_display), - left, top, + windowPosition.x, windowPosition.y, width, height, 0, visualInfo.depth, @@ -669,9 +680,11 @@ m_lastInputTime (0) { m_useSizeHints = true; XSizeHints* sizeHints = XAllocSizeHints(); - sizeHints->flags = PMinSize | PMaxSize; + sizeHints->flags = PMinSize | PMaxSize | USPosition; sizeHints->min_width = sizeHints->max_width = width; sizeHints->min_height = sizeHints->max_height = height; + sizeHints->x = windowPosition.x; + sizeHints->y = windowPosition.y; XSetWMNormalHints(m_display, m_window, sizeHints); XFree(sizeHints); } @@ -708,7 +721,15 @@ m_lastInputTime (0) // Set fullscreen video mode and switch to fullscreen if necessary if (m_fullscreen) { - setPosition(Vector2i(0, 0)); + // Disable hint for min and max size, + // otherwise some windows managers will not remove window decorations + XSizeHints *sizeHints = XAllocSizeHints(); + long flags = 0; + XGetWMNormalHints(m_display, m_window, sizeHints, &flags); + sizeHints->flags &= ~(PMinSize | PMaxSize); + XSetWMNormalHints(m_display, m_window, sizeHints); + XFree(sizeHints); + setVideoMode(mode); switchToFullscreen(); } @@ -722,11 +743,11 @@ WindowImplX11::~WindowImplX11() cleanup(); // Destroy icon pixmap - if(m_iconPixmap) + if (m_iconPixmap) XFreePixmap(m_display, m_iconPixmap); // Destroy icon mask pixmap - if(m_iconMaskPixmap) + if (m_iconMaskPixmap) XFreePixmap(m_display, m_iconMaskPixmap); // Destroy the cursor @@ -960,10 +981,10 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 return; } - if(m_iconPixmap) + if (m_iconPixmap) XFreePixmap(m_display, m_iconPixmap); - if(m_iconMaskPixmap) + if (m_iconMaskPixmap) XFreePixmap(m_display, m_iconMaskPixmap); m_iconPixmap = XCreatePixmap(m_display, RootWindow(m_display, m_screen), width, height, defDepth); @@ -1240,53 +1261,99 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) return; // Check if the XRandR extension is present - int version; - if (!XQueryExtension(m_display, "RANDR", &version, &version, &version)) + int xRandRMajor, xRandRMinor; + if (!checkXRandR(xRandRMajor, xRandRMinor)) { // XRandR extension is not supported: we cannot use fullscreen mode err() << "Fullscreen is not supported, switching to window mode" << std::endl; return; } - // Get the current configuration - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); + // Get root window + ::Window rootWindow = RootWindow(m_display, m_screen); - if (!config) + // Get the screen resources + XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow); + if (!res) { - // Failed to get the screen configuration - err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + err() << "Failed to get the current screen resources for fullscreen mode, switching to window mode" << std::endl; + return; + } + + RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor); + + // Get output info from output + XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output); + if (!outputInfo || outputInfo->connection == RR_Disconnected) + { + XRRFreeScreenResources(res); + + // If outputInfo->connection == RR_Disconnected, free output info + if (outputInfo) + XRRFreeOutputInfo(outputInfo); + + err() << "Failed to get output info for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Retreive current RRMode, screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to get crtc info for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Find RRMode to set + bool modeFound = false; + RRMode xRandMode; + + for (int i = 0; (i < res->nmode) && !modeFound; i++) + { + if (crtcInfo->rotation == RR_Rotate_90 || crtcInfo->rotation == RR_Rotate_270) + std::swap(res->modes[i].height, res->modes[i].width); + + // Check if screen size match + if (res->modes[i].width == static_cast(mode.width) && + res->modes[i].height == static_cast(mode.height)) + { + xRandMode = res->modes[i].id; + modeFound = true; + } + } + + if (!modeFound) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to find a matching RRMode for fullscreen mode, switching to window mode" << std::endl; return; } // Save the current video mode before we switch to fullscreen - Rotation currentRotation; - m_oldVideoMode = XRRConfigCurrentConfiguration(config, ¤tRotation); + m_oldVideoMode = crtcInfo->mode; + m_oldRRCrtc = outputInfo->crtc; - // Get the available screen sizes - int nbSizes; - XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); + // Switch to fullscreen mode + XRRSetCrtcConfig(m_display, + res, + outputInfo->crtc, + CurrentTime, + crtcInfo->x, + crtcInfo->y, + xRandMode, + crtcInfo->rotation, + &output, + 1); - // Search for a matching size - for (int i = 0; (sizes && i < nbSizes); ++i) - { - XRRConfigRotations(config, ¤tRotation); + // Set "this" as the current fullscreen window + fullscreenWindow = this; - if (currentRotation == RR_Rotate_90 || currentRotation == RR_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 - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), i, currentRotation, CurrentTime); - - // Set "this" as the current fullscreen window - fullscreenWindow = this; - break; - } - } - - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + XRRFreeCrtcInfo(crtcInfo); } @@ -1295,19 +1362,55 @@ void WindowImplX11::resetVideoMode() { if (fullscreenWindow == this) { - // Get current screen info - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); - if (config) + // Try to set old configuration + // Check if the XRandR extension + int xRandRMajor, xRandRMinor; + if (checkXRandR(xRandRMajor, xRandRMinor)) { - // Get the current rotation - Rotation currentRotation; - XRRConfigCurrentConfiguration(config, ¤tRotation); + XRRScreenResources* res = XRRGetScreenResources(m_display, DefaultRootWindow(m_display)); + if (!res) + { + err() << "Failed to get the current screen resources to reset the video mode" << std::endl; + return; + } - // Reset the video mode - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), m_oldVideoMode, currentRotation, CurrentTime); + // Retreive current screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, m_oldRRCrtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + err() << "Failed to get crtc info to reset the video mode" << std::endl; + return; + } - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + RROutput output; + + // if version >= 1.3 get the primary screen else take the first screen + if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1) + { + output = XRRGetOutputPrimary(m_display, DefaultRootWindow(m_display)); + + // Check if returned output is valid, otherwise use the first screen + if (output == None) + output = res->outputs[0]; + } + else{ + output = res->outputs[0]; + } + + XRRSetCrtcConfig(m_display, + res, + m_oldRRCrtc, + CurrentTime, + crtcInfo->x, + crtcInfo->y, + m_oldVideoMode, + crtcInfo->rotation, + &output, + 1); + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(res); } // Reset the fullscreen window @@ -1503,7 +1606,7 @@ void WindowImplX11::updateLastInputTime(::Time time) { Atom netWmUserTime = getAtom("_NET_WM_USER_TIME", true); - if(netWmUserTime) + if (netWmUserTime) { XChangeProperty(m_display, m_window, @@ -1973,6 +2076,106 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) return true; } + +//////////////////////////////////////////////////////////// +bool WindowImplX11::checkXRandR(int& xRandRMajor, int& xRandRMinor) +{ + // Check if the XRandR extension is present + int version; + if (!XQueryExtension(m_display, "RANDR", &version, &version, &version)) + { + err() << "XRandR extension is not supported" << std::endl; + return false; + } + + // Check XRandR version, 1.2 required + if (!XRRQueryVersion(m_display, &xRandRMajor, &xRandRMinor) || xRandRMajor < 1 || (xRandRMajor == 1 && xRandRMinor < 2 )) + { + err() << "XRandR is too old" << std::endl; + return false; + } + + return true; +} + + +//////////////////////////////////////////////////////////// +RROutput WindowImplX11::getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor) +{ + // if xRandR version >= 1.3 get the primary screen else take the first screen + if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1) + { + RROutput output = XRRGetOutputPrimary(m_display, rootWindow); + + // Check if returned output is valid, otherwise use the first screen + if (output == None) + return res->outputs[0]; + else + return output; + } + + // xRandr version can't get the primary screen, use the first screen + return res->outputs[0]; +} + + +//////////////////////////////////////////////////////////// +Vector2i WindowImplX11::getPrimaryMonitorPosition() +{ + Vector2i monitorPosition; + + // Get root window + ::Window rootWindow = RootWindow(m_display, m_screen); + + // Get the screen resources + XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow); + if (!res) + { + err() << "Failed to get the current screen resources for.primary monitor position" << std::endl; + return monitorPosition; + } + + // Get xRandr version + int xRandRMajor, xRandRMinor; + if (!checkXRandR(xRandRMajor, xRandRMinor)) + xRandRMajor = xRandRMinor = 0; + + RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor); + + // Get output info from output + XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output); + if (!outputInfo || outputInfo->connection == RR_Disconnected) + { + XRRFreeScreenResources(res); + + // If outputInfo->connection == RR_Disconnected, free output info + if (outputInfo) + XRRFreeOutputInfo(outputInfo); + + err() << "Failed to get output info for.primary monitor position" << std::endl; + return monitorPosition; + } + + // Retreive current RRMode, screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to get crtc info for.primary monitor position" << std::endl; + return monitorPosition; + } + + monitorPosition.x = crtcInfo->x; + monitorPosition.y = crtcInfo->y; + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeOutputInfo(outputInfo); + XRRFreeScreenResources(res); + + return monitorPosition; +} + } // namespace priv } // namespace sf diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 0ff694b1b..a025a1ae3 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -34,6 +34,7 @@ #include // Prevent conflict with macro None from Xlib #include #include +#include namespace sf @@ -264,6 +265,38 @@ private: //////////////////////////////////////////////////////////// bool processEvent(XEvent& windowEvent); + //////////////////////////////////////////////////////////// + /// \brief Check if a valid version of XRandR extension is present + /// + /// \param xRandRMajor XRandR major version + /// \param xRandRMinor XRandR minor version + /// + /// \return True if a valid XRandR version found, false otherwise + /// + //////////////////////////////////////////////////////////// + bool checkXRandR(int& xRandRMajor, int& xRandRMinor); + + //////////////////////////////////////////////////////////// + /// \brief Get the RROutput of the primary monitor + /// + /// \param rootWindow the root window + /// \param res screen resources + /// \param xRandRMajor XRandR major version + /// \param xRandRMinor XRandR minor version + /// + /// \return RROutput of the primary monitor + /// + //////////////////////////////////////////////////////////// + RROutput getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor); + + //////////////////////////////////////////////////////////// + /// \brief Get coordinates of the primary monitor + /// + /// \return Position of the primary monitor + /// + //////////////////////////////////////////////////////////// + Vector2i getPrimaryMonitorPosition(); + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// @@ -275,6 +308,7 @@ private: std::deque m_events; ///< Queue we use to store pending events for this window bool m_isExternal; ///< Tell whether the window has been created externally or by SFML int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid bool m_keyRepeat; ///< Is the KeyRepeat feature enabled?