-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix X11_SetWindowPosition() edgecase #4426
Conversation
src/video/x11/SDL_x11window.c
Outdated
@@ -818,9 +818,9 @@ X11_SetWindowPosition(_THIS, SDL_Window * window) | |||
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), | |||
attrs.x, attrs.y, &x, &y, &childReturn); | |||
|
|||
window->x = x; | |||
window->y = y; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The else if
right below this will break if it's set here, so while we should probably update this more aggressively it'll have to be at a different spot so the stuff below will be reachable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for taking a look! I force-pushed a fix. Should be good now.
cfa5ea0
to
f8115be
Compare
There is something else. I am not sure if we can do anything about it though. This 10ms waiting is really visible when window is being dragged sideways while brushing against bottom edge of the screen. Under what conditions issue worked around by loop triggers? For comparison GLFW does not do any of this, it just tries to set window position and calls it a day. WM is free to deny movement and that is also fine, since sdl-bug.mp4 |
It's possible that X specifically will have to get window position via events exclusively (since a lot of what makes this not work is all the roundtrips needed for X calls to stick), but keep in mind that Wayland's probably going to become the default within the next year or so, so either this will all get blown apart by Wayland anyway (but with no window positions at all, get or set!) or it'll be running through Xwayland, which is probably going to behave even worse than this. |
Hah we keep saying mainstream wayland next year for last five years, so i am not holding my breath.. I have been tinkering with the code more. 100ms hicups every time WM prevents window from moving is a bit much, especially for applications that use void
X11_SetWindowPosition(_THIS, SDL_Window * window)
{
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
Display *display = data->videodata->display;
unsigned int childCount;
Window childReturn, root, parent;
Window* children;
XWindowAttributes attrs;
int x, y, orig_x, orig_y;
Uint32 timeout;
X11_XSync(display, False);
X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &orig_x, &orig_y, &childReturn);
/*Attempt to move the window*/
X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
/* Wait a brief time to see if the window manager decided to let this move happen.
If the window changes at all, even to an unexpected value, we break out. */
//timeout = SDL_GetTicks() + 100;
//while (SDL_TRUE) {
X11_XSync(display, False);
X11_XGetWindowAttributes(display, data->xwindow, &attrs);
X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
attrs.x, attrs.y, &x, &y, &childReturn);
// if ((x != orig_x) || (y != orig_y)) {
// break; /* window moved, time to go. */
// } else if ((x == window->x) && (y == window->y)) {
// break; /* we're at the place we wanted to be anyhow, drop out. */
// }
//
// if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) {
// break;
// }
//
// SDL_Delay(10);
//}
window->x = x;
window->y = y;
} This seems to work much better in my case. I am sure this code was added for a good reason and i would love to find out exact reason so i could replicate issue this loop is fixing. I read through #3337 and #3269 and pulled out old issue reproduction case and played with it. Since i we are dealing with position issues here as opposed to size issues that #3337 describes - i repurposed it a bit. Testcase: #include <SDL.h>
#include <stdio.h>
using namespace std;
int main (int argc, char** argv)
{
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) return -1;
SDL_Window* window = SDL_CreateWindow("",0, 0,640, 480,0);
if (!window) return -1;
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
if (!renderer) return -1;
//SDL_Event e; while (SDL_PollEvent(&e));//This line fixes SDL_GetWindowSize,
//but not the drawing problem
int w, h;
SDL_SetWindowPosition(window, 600, 2300);
printf("SDL_SetWindowPosition(600, 2300); // Request window position way out of bounds\n");
SDL_GetWindowPosition(window, &w,&h);
printf("SDL_GetWindowPosition(%d, %d); // Initial window position response\n", w, h);
SDL_PollEvent(nullptr); //This line causes next SDL_GetWindowSize to be wrong
//Uncomment the next 3 lines for a (weird, possibly unreliable) fix
// for the drawing problem
//Replacing the loop with a single SDL_Delay(60); seems less reliable
//SDL_RenderPresent(renderer);
//for (int i = 0; i < 6; ++i) //Must be 6 or more to be reliable
// SDL_Delay(10); //10 works; 1 doesn't
SDL_GetWindowPosition(window, &w, &h);
printf("SDL_GetWindowPosition(%d, %d); // Final window position response\n", w, h);
//Now let's draw cross hairs centered on middle
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawLine (renderer, 0, h/2, w, h/2);
SDL_RenderDrawLine (renderer, w/2, 0, w/2, h);
SDL_RenderPresent (renderer);
SDL_Event sdlEvent; //Wait for key hit, so we can see rect
do
SDL_WaitEvent (&sdlEvent);
while (sdlEvent.type != SDL_KEYDOWN);
return 0;
} Result:
So yes, you are right. We have a race condition here. Move request is not processed immediately and we get |
You can update this patch to do this instead, if you want. Both changes seem okay to me, the other way makes sense too provided the sync does what it's supposed to do. For Wayland-by-default, see #4306. Once libdecor is frozen (expected this year) and NV 470.xx is out (expected this month) this flip will happen really soon after for both SDL and desktop environments, possibly as soon as 2.0.18 should testers not find anything incorrect with our implementation. I strongly suggest testing ImGui_SDL against both drivers. |
Great! I will do that! I wasnt sure it will be accepted. It looks ok on the surface, but everything always is a deep pit.. Good to have a second opinion that agrees 👍🏻
Yeah.. This is bit complicated. Dear ImGui currently expects to know global window position to work and wayland threw us under the bus in this regard. I am pushing for a path to support wayland nevertheless, but its a tough way ahead. Hopefully we will get there 👍🏻 Edit: |
Currently it is not possible to move window with `SDL_WINDOW_BORDERLESS` flag outside of screen bounds using `SDL_SetWindowPosition()` (libsdl-org#3813). Suppose we move such window at the bottom of the screen, and it's bottom border is stops at bottom border of the screen. If we proceed to move such window further out of the screen bounds (still using `SDL_SetWindowPosition()`), window position keeps getting updated in `SDL_SetWindowPosition()`, but position we requested is outside of screen bounds and therefore is not valid. WM forces our window to stay in the same position. When `X11_SetWindowPosition()` executes following happens: 1. `X11_XTranslateCoordinates(&orig_x, &orig_y);` - return position clamped within screen bounds. This is not what we requested in `X11_SetWindowPosition()` and not what `window->x`/`window->y` have currently set. 2. `X11_XMoveWindow();` - move to requested coordinates. However nothing happens because WM prevents moving outside of screen bounds. Window remains in the same location. 3. `X11_XTranslateCoordinates(&x, &y);` - checking if window moved. This call returns same coordinates as in step 1. 4. `if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout))` - eventually check passes and we break out of the loop. So `SDL_SetWindowPosition()` updated `window->x`/`window->y`, then we failed to move window and kept our requested window position set in `SDL_Window`, making subsequent `SDL_GetWindowPosition()` calls return incorrect results. At the same time we get 100ms stalls when WM prevents window movement. Solution: unconditionally update window struct with real window position while removing loop. This change may result in `SDL_GetWindowPosition()` returning different than requested coordinates if called right after `SDL_SetWindowPosition()`, however this is OK. We have no guarantees `SDL_SetWindowPosition()` would place a window at requested position anyhow. If WM continues to move window after `SDL_SetWindowPosition()` - these updates will be taken into account by `ConfigureNotify` event handler.
f8115be
to
eb0c751
Compare
I'm going to bump this from 2.26.0, since there's a lot of chatter in this thread and the current patch needs conflicts resolved, so let's not risk this right at the end of the milestone. |
Having looked at this, I'm going to decline to change this; SDL takes a lot of pains to deal with X11's async nature but also present a consistent API, and removing that code is going to cause problems that we've tackled several times now. We can revisit this in SDL3 if there's a specific problem with borderless windows, but this patch isn't the way we want to handle it. |
Currently it is not possible to move window with
SDL_WINDOW_BORDERLESS
flag outside of screen bounds usingSDL_SetWindowPosition()
(#3813). Suppose we move such window at the bottom of the screen, and it's bottom border is stops at bottom border of the screen. If we proceed to move such window further out of the screen bounds (still usingSDL_SetWindowPosition()
), window position keeps getting updated inSDL_SetWindowPosition()
, but position we requested is outside of screen bounds and therefore is not valid. WM forces our window to stay in the same position. WhenX11_SetWindowPosition()
executes following happens:X11_XTranslateCoordinates(&orig_x, &orig_y);
- return position clamped within screen bounds. This is not what we requested inX11_SetWindowPosition()
and not whatwindow->x
/window->y
have currently set.X11_XMoveWindow();
- move to requested coordinates. However nothing happens because WM prevents moving outside of screen bounds. Window remains in the same location.X11_XTranslateCoordinates(&x, &y);
- checking if window moved. This call returns same coordinates as in step 1.if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout))
- eventually check passes and we break out of the loop.So
SDL_SetWindowPosition()
updatedwindow->x
/window->y
, then we failed to move window and kept our requested window position set inSDL_Window
, making subsequentSDL_GetWindowPosition()
calls return incorrect results.Solution: unconditionally update window struct with real window position, because why not.
To reproduce this issue do the following:
Result: It becomes impossible to click widgets because
SDL_GetWindowPos()
returns position that does not match actual window position.bug.mp4