Fix crash on exit under Wayland with multiple windows open

Exiting with multiple overlapping windows (a file selector for e.g.)
reliably crashes.

Closing the windows on exit caused the the keyboard enter handler to be
called with a NULL window surface (wl_surface).

While this doesn't look to be documented anywhere, SDL's code-comments
note this happens when windows have just been closed.
GTK also check surfaces for NULL.
This commit is contained in:
Campbell Barton
2023-05-19 16:08:59 +10:00
parent 2eaacbaa3f
commit 2ce7f3ec71
2 changed files with 23 additions and 9 deletions

View File

@@ -2308,7 +2308,7 @@ static void data_device_handle_enter(void *data,
const wl_fixed_t y,
struct wl_data_offer *id)
{
if (!ghost_wl_surface_own(wl_surface)) {
if (!ghost_wl_surface_own_with_null_check(wl_surface)) {
CLOG_INFO(LOG, 2, "enter (skipped)");
return;
}
@@ -2643,7 +2643,8 @@ static void pointer_handle_enter(void *data,
const wl_fixed_t surface_x,
const wl_fixed_t surface_y)
{
if (!ghost_wl_surface_own(wl_surface)) {
/* Null when just destroyed. */
if (!ghost_wl_surface_own_with_null_check(wl_surface)) {
CLOG_INFO(LOG, 2, "enter (skipped)");
return;
}
@@ -2682,12 +2683,11 @@ static void pointer_handle_leave(void *data,
{
/* First clear the `pointer.wl_surface`, since the window won't exist when closing the window. */
static_cast<GWL_Seat *>(data)->pointer.wl_surface_window = nullptr;
if (wl_surface && ghost_wl_surface_own(wl_surface)) {
CLOG_INFO(LOG, 2, "leave");
}
else {
if (!ghost_wl_surface_own_with_null_check(wl_surface)) {
CLOG_INFO(LOG, 2, "leave (skipped)");
return;
}
CLOG_INFO(LOG, 2, "leave");
}
static void pointer_handle_motion(void *data,
@@ -3324,7 +3324,7 @@ static void tablet_tool_handle_proximity_in(void *data,
struct zwp_tablet_v2 * /*tablet*/,
struct wl_surface *wl_surface)
{
if (!ghost_wl_surface_own(wl_surface)) {
if (!ghost_wl_surface_own_with_null_check(wl_surface)) {
CLOG_INFO(LOG, 2, "proximity_in (skipped)");
return;
}
@@ -3743,7 +3743,8 @@ static void keyboard_handle_enter(void *data,
struct wl_surface *wl_surface,
struct wl_array *keys)
{
if (!ghost_wl_surface_own(wl_surface)) {
/* Null when just destroyed. */
if (!ghost_wl_surface_own_with_null_check(wl_surface)) {
CLOG_INFO(LOG, 2, "enter (skipped)");
return;
}
@@ -3785,7 +3786,7 @@ static void keyboard_handle_leave(void *data,
const uint32_t /*serial*/,
struct wl_surface *wl_surface)
{
if (!(wl_surface && ghost_wl_surface_own(wl_surface))) {
if (!ghost_wl_surface_own_with_null_check(wl_surface)) {
CLOG_INFO(LOG, 2, "leave (skipped)");
return;
}
@@ -6862,6 +6863,11 @@ bool ghost_wl_surface_own(const struct wl_surface *wl_surface)
return wl_proxy_get_tag((struct wl_proxy *)wl_surface) == &ghost_wl_surface_tag_id;
}
bool ghost_wl_surface_own_with_null_check(const struct wl_surface *wl_surface)
{
return wl_surface && ghost_wl_surface_own(wl_surface);
}
bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *wl_surface)
{
return wl_proxy_get_tag((struct wl_proxy *)wl_surface) ==

View File

@@ -37,6 +37,14 @@ bool ghost_wl_output_own(const struct wl_output *wl_output);
void ghost_wl_output_tag(struct wl_output *wl_output);
struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output);
/**
* Enter/exit handlers may be called with a null window surface (when the window just closed),
* so add a version of the function that checks this.
*
* All of the functions could in fact however paranoid null checks make the expected
* state difficult to reason about, so only use this in cases the surface may be null.
*/
bool ghost_wl_surface_own_with_null_check(const struct wl_surface *wl_surface);
bool ghost_wl_surface_own(const struct wl_surface *wl_surface);
void ghost_wl_surface_tag(struct wl_surface *wl_surface);
GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface);