From aaccf3cd52882d3ac227541976cd90b75a2f15b5 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Fri, 9 Jun 2023 14:21:17 +0200 Subject: [PATCH] X11: common function to wait for the WM before sending SDL Window events --- src/video/x11/SDL_x11window.c | 321 +++++++++++++--------------------- 1 file changed, 123 insertions(+), 198 deletions(-) diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index eb7f182b5e..d15e206608 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -825,6 +825,109 @@ static int X11_CatchAnyError(Display *d, XErrorEvent *e) return 0; } +enum check_method { + COMPARE_POSITION = 1, + COMPARE_SIZE = 2, + COMPARE_DOUBLE_ATTEMPT = 3, + COMPARE_ORIG = 4, + COMPARE_NO_WAIT = 5 +}; +/* Wait a brief time, or not, to see if the window manager decided to move/resize the window. + * Send MOVED and RESIZED window events */ +static void X11_WaitAndSendWindowEvents(SDL_Window *window, int param_timeout, enum check_method method, + int orig_x, int orig_y, int dest_x, int dest_y, + int orig_w, int orig_h, int dest_w, int dest_h) +{ + SDL_WindowData *data = window->driverdata; + Display *display = data->videodata->display; + Window xwindow = data->xwindow; + int (*prev_handler)(Display *, XErrorEvent *); + int x, y; + XWindowAttributes attrs; + Uint64 timeout = 0; + Window childReturn, root, parent; + Window *children; + unsigned int childCount; + SDL_bool window_size_changed = SDL_FALSE; + int window_position_changed = 0; + + X11_XSync(display, False); + prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); + + if (method != COMPARE_NO_WAIT) { + timeout = SDL_GetTicks() + param_timeout; + } + + /* Get the parent */ + X11_XQueryTree(display, xwindow, &root, &parent, &children, &childCount); + + while (SDL_TRUE) { + caught_x11_error = SDL_FALSE; + X11_XSync(display, False); + X11_XGetWindowAttributes(display, xwindow, &attrs); + X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), + attrs.x, attrs.y, &x, &y, &childReturn); + + if (method == COMPARE_NO_WAIT) { + break; + } + + if (!caught_x11_error) { + if (method == COMPARE_POSITION) { + if (x != orig_x || y != orig_y) { + break; /* window moved, time to go. */ + } else if (x == dest_x && y == dest_y) { + break; /* we're at the place we wanted to be anyhow, drop out. */ + } + } else if (method == COMPARE_SIZE) { + if (attrs.width != orig_w || attrs.height != orig_h) { + break; /* window changed, time to go. */ + } else if (attrs.width == window->w && attrs.height == window->h) { + break; /* we've size we wanted anyhow, drop out. */ + } + } else if (method == COMPARE_ORIG) { + if (x != orig_x || y != orig_y || attrs.width != orig_w || attrs.height != orig_h) { + break; /* window moved or resized, time to go. */ + } + } else if (method == COMPARE_DOUBLE_ATTEMPT) { + if (x != orig_x || y != orig_y) { + orig_x = x; + orig_y = y; + window_position_changed += 1; + } + if (attrs.width != orig_w || attrs.height != orig_h) { + orig_w = attrs.width; + orig_h = attrs.height; + window_size_changed = SDL_TRUE; + } + /* Wait for at least 2 moves + 1 size changed to have valid values */ + if (window_position_changed >= 2 && window_size_changed) { + break; /* window changed, time to go. */ + } + } + } + + if (SDL_GetTicks() >= timeout) { + break; + } + + SDL_Delay(10); + } + + if (!caught_x11_error) { + if (SDL_WINDOW_IS_POPUP(window)) { + SDL_GlobalToRelativeForWindow(window, x, y, &x, &y); + } + + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); + SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); + } + + X11_XSetErrorHandler(prev_handler); + caught_x11_error = SDL_FALSE; +} + + int X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon) { SDL_WindowData *data = window->driverdata; @@ -888,15 +991,12 @@ void X11_UpdateWindowPosition(SDL_Window *window) SDL_Window *w; SDL_WindowData *data = window->driverdata; Display *display = data->videodata->display; - int (*prev_handler)(Display *, XErrorEvent *) = NULL; unsigned int childCount; Window childReturn, root, parent; Window *children; XWindowAttributes attrs; - int x, y; int dest_x, dest_y; int orig_x, orig_y; - Uint64 timeout; X11_XSync(display, False); X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount); @@ -911,45 +1011,8 @@ void X11_UpdateWindowPosition(SDL_Window *window) /* Attempt to move the window */ X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); - /* 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. */ - X11_XSync(display, False); - prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); - - timeout = SDL_GetTicks() + 100; - while (SDL_TRUE) { - caught_x11_error = SDL_FALSE; - X11_XSync(display, False); - X11_XGetWindowAttributes(display, data->xwindow, &attrs); - X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), - attrs.x, attrs.y, &x, &y, &childReturn); - - if (!caught_x11_error) { - if ((x != orig_x) || (y != orig_y)) { - break; /* window moved, time to go. */ - } else if ((x == dest_x) && (y == dest_y)) { - break; /* we're at the place we wanted to be anyhow, drop out. */ - } - } - - if (SDL_GetTicks() >= timeout) { - break; - } - - SDL_Delay(10); - } - - if (!caught_x11_error) { - if (SDL_WINDOW_IS_POPUP(window)) { - SDL_GlobalToRelativeForWindow(window, x, y, &x, &y); - } - - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); - } - - X11_XSetErrorHandler(prev_handler); - caught_x11_error = SDL_FALSE; + /* Send MOVED/RESIZED event, if needed. Compare with initial/expected position. Timeout 100 */ + X11_WaitAndSendWindowEvents(window, 100, COMPARE_POSITION, orig_x, orig_y, dest_x, dest_y, 0, 0, 0, 0); for (w = window->first_child; w != NULL; w = w->next_sibling) { X11_UpdateWindowPosition(w); @@ -1048,10 +1111,8 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) { SDL_WindowData *data = window->driverdata; Display *display = data->videodata->display; - int (*prev_handler)(Display *, XErrorEvent *) = NULL; XWindowAttributes attrs; int orig_w, orig_h; - Uint64 timeout; X11_XSync(display, False); X11_XGetWindowAttributes(display, data->xwindow, &attrs); @@ -1077,47 +1138,18 @@ void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) X11_XResizeWindow(display, data->xwindow, window->w, window->h); } - X11_XSync(display, False); - prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); + /* Timeout occurred and window size didn't change + * window manager likely denied the resize, + * or the new size is the same as the existing: + * - current width: is 'full width'. + * - try to set new width at 'full width + 1', which get truncated to 'full width'. + * - new width is/remains 'full width' + * So, even if we break here as a timeout, we can send an event, since the requested size isn't the same + * as the final size. (even if final size is same as original size). + */ - /* Wait a brief time to see if the window manager decided to let this resize happen. - If the window changes at all, even to an unexpected value, we break out. */ - timeout = SDL_GetTicks() + 100; - while (SDL_TRUE) { - caught_x11_error = SDL_FALSE; - X11_XSync(display, False); - X11_XGetWindowAttributes(display, data->xwindow, &attrs); - - if (!caught_x11_error) { - if ((attrs.width != orig_w) || (attrs.height != orig_h)) { - break; /* window changed, time to go. */ - } else if ((attrs.width == window->w) && (attrs.height == window->h)) { - break; /* we're at the place we wanted to be anyhow, drop out. */ - } - } - - if (SDL_GetTicks() >= timeout) { - /* Timeout occurred and window size didn't change - * window manager likely denied the resize, - * or the new size is the same as the existing: - * - current width: is 'full width'. - * - try to set new width at 'full width + 1', which get truncated to 'full width'. - * - new width is/remains 'full width' - * So, even if we break here as a timeout, we can send an event, since the requested size isn't the same - * as the final size. (even if final size is same as original size). - */ - break; - } - - SDL_Delay(10); - } - - if (!caught_x11_error) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); - } - - X11_XSetErrorHandler(prev_handler); - caught_x11_error = SDL_FALSE; + /* Send MOVED/RESIZED event, if needed. Compare with initial/expected size. Timeout 100 */ + X11_WaitAndSendWindowEvents(window, 100, COMPARE_SIZE, 0, 0, 0, 0, orig_w, orig_h, window->w, window->h); } int X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right) @@ -1340,32 +1372,8 @@ void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) } } - /* Check if the window manager moved us somewhere unexpected, just in case. */ - { - int (*prev_handler)(Display *, XErrorEvent *) = NULL; - Window childReturn, root, parent; - Window *children; - unsigned int childCount; - XWindowAttributes attrs; - int x, y; - - X11_XSync(display, False); - prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); - caught_x11_error = SDL_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, &x, &y, &childReturn); - - if (!caught_x11_error) { - /* if these values haven't changed from our current beliefs, these don't actually generate events. */ - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); - } - - X11_XSetErrorHandler(prev_handler); - caught_x11_error = SDL_FALSE; - } + /* Send MOVED/RESIZED event, if needed. Immediate, no timeout */ + X11_WaitAndSendWindowEvents(window, 0, COMPARE_NO_WAIT, 0, 0, 0, 0, 0, 0, 0, 0); } static void X11_SetWindowActive(SDL_VideoDevice *_this, SDL_Window *window) @@ -1430,15 +1438,11 @@ static void X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, S } if (X11_IsWindowMapped(_this, window)) { - /* !!! FIXME: most of this waiting code is copy/pasted from elsewhere. */ - int (*prev_handler)(Display *, XErrorEvent *) = NULL; XWindowAttributes attrs; Window childReturn, root, parent; Window *children; unsigned int childCount; int orig_w, orig_h, orig_x, orig_y; - int x, y; - Uint64 timeout; XEvent e; X11_XSync(display, False); @@ -1463,39 +1467,10 @@ static void X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, S X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, SubstructureNotifyMask | SubstructureRedirectMask, &e); - /* Wait a brief time to see if the window manager decided to let this happen. - If the window changes at all, even to an unexpected value, we break out. */ - X11_XSync(display, False); - prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); + /* Send MOVED/RESIZED event, if needed. Compare with initial position and size. Timeout 1000 */ + X11_WaitAndSendWindowEvents(window, 1000, COMPARE_ORIG, orig_x, orig_y, 0, 0, orig_w, orig_h, 0, 0); - timeout = SDL_GetTicks() + 1000; - while (SDL_TRUE) { - caught_x11_error = SDL_FALSE; - X11_XSync(display, False); - X11_XGetWindowAttributes(display, data->xwindow, &attrs); - X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), - attrs.x, attrs.y, &x, &y, &childReturn); - if (!caught_x11_error) { - if ((x != orig_x) || (y != orig_y) || (attrs.width != orig_w) || (attrs.height != orig_h)) { - break; /* window changed, time to go. */ - } - } - - if (SDL_GetTicks() >= timeout) { - break; - } - - SDL_Delay(10); - } - - if (!caught_x11_error) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); - } - - X11_XSetErrorHandler(prev_handler); - caught_x11_error = SDL_FALSE; } else { X11_SetNetWMState(_this, data->xwindow, window->flags); } @@ -1532,20 +1507,14 @@ static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *win Display *display = data->videodata->display; Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE; Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN; - SDL_bool window_size_changed = SDL_FALSE; - int window_position_changed = 0; if (X11_IsWindowMapped(_this, window)) { XEvent e; - /* !!! FIXME: most of this waiting code is copy/pasted from elsewhere. */ - int (*prev_handler)(Display *, XErrorEvent *) = NULL; unsigned int childCount; Window childReturn, root, parent; Window *children; XWindowAttributes attrs; - int x, y; int orig_w, orig_h, orig_x, orig_y; - Uint64 timeout; X11_XSync(display, False); X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount); @@ -1623,54 +1592,10 @@ static void X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *win X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); } + /* Send MOVED/RESIZED event, if needed. Compare with initial position and size. Timeout 100 */ + /* Wait for at least 2 moves + 1 size changed to have valid values */ + X11_WaitAndSendWindowEvents(window, 100, COMPARE_DOUBLE_ATTEMPT, orig_x, orig_y, 0, 0, orig_w, orig_h, 0, 0); - /* Wait a brief time to see if the window manager decided to let this happen. - If the window changes at all, even to an unexpected value, we break out. */ - X11_XSync(display, False); - prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); - - timeout = SDL_GetTicks() + 100; - while (SDL_TRUE) { - - caught_x11_error = SDL_FALSE; - X11_XSync(display, False); - X11_XGetWindowAttributes(display, data->xwindow, &attrs); - X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), - attrs.x, attrs.y, &x, &y, &childReturn); - - if (!caught_x11_error) { - if ((x != orig_x) || (y != orig_y)) { - orig_x = x; - orig_y = y; - window_position_changed += 1; - } - - if ((attrs.width != orig_w) || (attrs.height != orig_h)) { - orig_w = attrs.width; - orig_h = attrs.height; - window_size_changed = SDL_TRUE; - } - - /* Wait for at least 2 moves + 1 size changed to have valid values */ - if (window_position_changed >= 2 && window_size_changed) { - break; /* window changed, time to go. */ - } - } - - if (SDL_GetTicks() >= timeout) { - break; - } - - SDL_Delay(10); - } - - if (!caught_x11_error) { - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); - SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); - } - - X11_XSetErrorHandler(prev_handler); - caught_x11_error = SDL_FALSE; } else { Uint32 flags;