Created
August 20, 2017 01:17
-
-
Save llongi/c993113f0fc31db638af22a3c4784563 to your computer and use it in GitHub Desktop.
SFML fix issue #1228 V3 (cleaner, EWMH, lots of testing)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp | |
index 5712f04b..ed7c201d 100644 | |
--- a/src/SFML/Window/Unix/WindowImplX11.cpp | |
+++ b/src/SFML/Window/Unix/WindowImplX11.cpp | |
@@ -66,6 +66,8 @@ namespace | |
sf::Mutex allWindowsMutex; | |
sf::String windowManagerName; | |
+ sf::String wmAbsPosGood[] = { "Enlightenment", "FVWM", "i3" }; | |
+ | |
static const unsigned long eventMask = FocusChangeMask | ButtonPressMask | | |
ButtonReleaseMask | ButtonMotionMask | | |
PointerMotionMask | KeyPressMask | | |
@@ -260,6 +262,99 @@ namespace | |
return true; | |
} | |
+ // Get the parent window. | |
+ ::Window getParentWindow(::Display *disp, ::Window win) | |
+ { | |
+ ::Window root, parent, *children = NULL; | |
+ unsigned int numChildren; | |
+ | |
+ XQueryTree(disp, win, &root, &parent, &children, &numChildren); | |
+ if (children != NULL) | |
+ { | |
+ XFree(children); // Not used. | |
+ } | |
+ | |
+ return parent; | |
+ } | |
+ | |
+ // Get the Frame Extents from EWMH WMs that support it. | |
+ bool getEWMHFrameExtents(::Display *disp, ::Window win, | |
+ long &xFrameExtent, long &yFrameExtent) | |
+ { | |
+ if (!ewmhSupported()) | |
+ { | |
+ return false; | |
+ } | |
+ | |
+ Atom frameExtents = sf::priv::getAtom("_NET_FRAME_EXTENTS", true); | |
+ | |
+ if (frameExtents == None) | |
+ { | |
+ return false; | |
+ } | |
+ | |
+ bool gotFrameExtents = false; | |
+ Atom actualType; | |
+ int actualFormat; | |
+ unsigned long numItems; | |
+ unsigned long numBytesLeft; | |
+ unsigned char *data = NULL; | |
+ | |
+ int result = XGetWindowProperty(disp, | |
+ win, | |
+ frameExtents, | |
+ 0, | |
+ 4, | |
+ False, | |
+ XA_CARDINAL, | |
+ &actualType, | |
+ &actualFormat, | |
+ &numItems, | |
+ &numBytesLeft, | |
+ &data); | |
+ | |
+ if (result == Success && actualType == XA_CARDINAL && | |
+ actualFormat == 32 && numItems == 4 && numBytesLeft == 0 && | |
+ data != NULL) | |
+ { | |
+ gotFrameExtents = true; | |
+ | |
+ long *extents = (long *) data; | |
+ | |
+ xFrameExtent = extents[0]; // Left. | |
+ yFrameExtent = extents[2]; // Top. | |
+ } | |
+ | |
+ // Always free data. | |
+ if (data != NULL) | |
+ { | |
+ XFree(data); | |
+ } | |
+ | |
+ return gotFrameExtents; | |
+ } | |
+ | |
+ // Check if the current WM is in the list of good WMs that provide | |
+ // a correct absolute position for the window when queried. | |
+ bool isWMAbsolutePositionGood() | |
+ { | |
+ // This can only work with EWMH, to get the name. | |
+ if (!ewmhSupported()) | |
+ { | |
+ return false; | |
+ } | |
+ | |
+ for (size_t i = 0; i < (sizeof(wmAbsPosGood) / sizeof(wmAbsPosGood[0])); i++) | |
+ { | |
+ if (wmAbsPosGood[i] == windowManagerName) | |
+ { | |
+ return true; | |
+ } | |
+ } | |
+ | |
+ return false; | |
+ } | |
+ | |
sf::Keyboard::Key keysymToSF(KeySym symbol) | |
{ | |
switch (symbol) | |
@@ -676,14 +771,68 @@ void WindowImplX11::processEvents() | |
//////////////////////////////////////////////////////////// | |
Vector2i WindowImplX11::getPosition() const | |
{ | |
- ::Window root, child; | |
- int localX, localY, x, y; | |
- unsigned int width, height, border, depth; | |
+ // Get absolute position of our window relative to root window. This | |
+ // takes into account all information that X11 has, including X11 | |
+ // border widths and any decorations. It corresponds to where the | |
+ // window actually is, but not necessarily to where we told it to | |
+ // go using setPosition() and XMoveWindow(). To have the two match | |
+ // as expected, we may have to subtract decorations and borders. | |
+ ::Window child; | |
+ int xAbsRelToRoot, yAbsRelToRoot; | |
+ | |
+ XTranslateCoordinates(m_display, m_window, DefaultRootWindow(m_display), | |
+ 0, 0, &xAbsRelToRoot, &yAbsRelToRoot, &child); | |
+ | |
+ // CASE 1: some rare WMs actually put the window exactly where we tell | |
+ // it to, even with decorations and such, which get shifted back. | |
+ // In these rare cases, we can use the absolute value directly. | |
+ if (isWMAbsolutePositionGood()) { | |
+ return Vector2i(xAbsRelToRoot, yAbsRelToRoot); | |
+ } | |
+ | |
+ // CASE 2: most modern WMs support EWMH and can define _NET_FRAME_EXTENTS | |
+ // with the exact frame size to subtract, so if present, we prefer it and | |
+ // query it first. According to spec, this already includes any borders. | |
+ long xFrameExtent, yFrameExtent; | |
+ | |
+ if (getEWMHFrameExtents(m_display, m_window, xFrameExtent, yFrameExtent)) | |
+ { | |
+ // Get final X/Y coordinates: subtract EWMH frame extents from | |
+ // absolute window position. | |
+ return Vector2i(xAbsRelToRoot - xFrameExtent, yAbsRelToRoot - yFrameExtent); | |
+ } | |
+ | |
+ // CASE 3: EWMH frame extents were not available, use geometry. | |
+ // We climb back up to the window before the root and use its | |
+ // geometry information to extract X/Y position. This because | |
+ // re-parenting WMs may re-parent the window multiple times, so | |
+ // we'd have to climb up to the furthest ancestor and sum the | |
+ // relative differences and borders anyway; and doing that to | |
+ // subtract those values from the absolute coordinates of the | |
+ // window is equivalent to going up the tree and asking the | |
+ // furthest ancestor what it's relative distance to the root is. | |
+ // So we use that approach because it's simpler. | |
+ // This approach assumes that any window between the root and | |
+ // our window is part of decorations/borders in some way. This | |
+ // seems to hold true for most reasonable WM implementations. | |
+ ::Window ancestor = m_window; | |
+ ::Window root = DefaultRootWindow(m_display); | |
+ | |
+ while (getParentWindow(m_display, ancestor) != root) | |
+ { | |
+ // Next window up (parent window). | |
+ ancestor = getParentWindow(m_display, ancestor); | |
+ } | |
+ | |
+ // Get final X/Y coordinates: take the relative position to | |
+ // the root of the furthest ancestor window. | |
+ int xRelToRoot, yRelToRoot; | |
+ unsigned int width, height, borderWidth, depth; | |
- XGetGeometry(m_display, m_window, &root, &localX, &localY, &width, &height, &border, &depth); | |
- XTranslateCoordinates(m_display, m_window, root, localX, localY, &x, &y, &child); | |
+ XGetGeometry(m_display, ancestor, &root, &xRelToRoot, &yRelToRoot, | |
+ &width, &height, &borderWidth, &depth); | |
- return Vector2i(x, y); | |
+ return Vector2i(xRelToRoot, yRelToRoot); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment