Fix BadWindow X11 errors when a window is closed while processing struts

A window can be closed on the server side while processing results from
_NET_CLIENT_LIST, which causes BadWindow fatal errors by default in
XGetWindowProperty.

The only way to safely catch this case is to set an error handler to
ignore BadWindow errors while these commands are processed.
This commit is contained in:
PouleyKetchoupp 2021-11-04 09:47:18 -07:00
parent 7b83039885
commit 4215d694f8

View file

@ -733,6 +733,16 @@ Size2i DisplayServerX11::screen_get_size(int p_screen) const {
return _screen_get_rect(p_screen).size; return _screen_get_rect(p_screen).size;
} }
bool g_bad_window = false;
int bad_window_error_handler(Display *display, XErrorEvent *error) {
if (error->error_code == BadWindow) {
g_bad_window = true;
} else {
ERR_PRINT("Unhandled XServer error code: " + itos(error->error_code));
}
return 0;
}
Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const { Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
@ -869,7 +879,13 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
if (desktop_valid) { if (desktop_valid) {
use_simple_method = false; use_simple_method = false;
// Handle bad window errors silently because there's no other way to check
// that one of the windows has been destroyed in the meantime.
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&bad_window_error_handler);
for (unsigned long win_index = 0; win_index < clients_len; ++win_index) { for (unsigned long win_index = 0; win_index < clients_len; ++win_index) {
g_bad_window = false;
// Remove strut size from desktop size to get a more accurate result. // Remove strut size from desktop size to get a more accurate result.
bool strut_found = false; bool strut_found = false;
unsigned long strut_len = 0; unsigned long strut_len = 0;
@ -881,7 +897,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
} }
} }
// Fallback to older strut property. // Fallback to older strut property.
if (!strut_found) { if (!g_bad_window && !strut_found) {
Atom strut_prop = XInternAtom(x11_display, "_NET_WM_STRUT", True); Atom strut_prop = XInternAtom(x11_display, "_NET_WM_STRUT", True);
if (strut_prop != None) { if (strut_prop != None) {
if (XGetWindowProperty(x11_display, windows_data[win_index], strut_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &strut_len, &remaining, &strut_data) == Success) { if (XGetWindowProperty(x11_display, windows_data[win_index], strut_prop, 0, LONG_MAX, False, XA_CARDINAL, &type, &format, &strut_len, &remaining, &strut_data) == Success) {
@ -889,7 +905,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
} }
} }
} }
if (strut_found && (format == 32) && (strut_len >= 4) && strut_data) { if (!g_bad_window && strut_found && (format == 32) && (strut_len >= 4) && strut_data) {
long *struts = (long *)strut_data; long *struts = (long *)strut_data;
long left = struts[0]; long left = struts[0];
@ -961,6 +977,9 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
XFree(strut_data); XFree(strut_data);
} }
} }
// Restore default error handler.
XSetErrorHandler(oldHandler);
} }
} }
} }