wayland: Use libdecor visibility toggle for hiding/showing the window The current method of toggling the libdecor window visibility by destroying and recreating the frame results in a race where a use-after-free bug can manifest itself within libdecor when window visibility is toggled quickly. Instead, use the libdecor function for toggling visibility instead of destroying and recreating the frame every time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index c5b997c..a94063d 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -1228,15 +1228,20 @@ void Wayland_ShowWindow(_THIS, SDL_Window *window)
/* Create the shell surface and map the toplevel/popup */
#ifdef HAVE_LIBDECOR_H
if (WINDOW_IS_LIBDECOR(c, window)) {
- data->shell_surface.libdecor.frame = libdecor_decorate(c->shell.libdecor,
- data->surface,
- &libdecor_frame_interface,
- data);
- if (data->shell_surface.libdecor.frame == NULL) {
- SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Failed to create libdecor frame!");
+ if (data->shell_surface.libdecor.frame) {
+ /* If the frame already exists, just set the visibility. */
+ libdecor_frame_set_visibility(data->shell_surface.libdecor.frame, true);
} else {
- libdecor_frame_set_app_id(data->shell_surface.libdecor.frame, c->classname);
- libdecor_frame_map(data->shell_surface.libdecor.frame);
+ data->shell_surface.libdecor.frame = libdecor_decorate(c->shell.libdecor,
+ data->surface,
+ &libdecor_frame_interface,
+ data);
+ if (data->shell_surface.libdecor.frame == NULL) {
+ SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Failed to create libdecor frame!");
+ } else {
+ libdecor_frame_set_app_id(data->shell_surface.libdecor.frame, c->classname);
+ libdecor_frame_map(data->shell_surface.libdecor.frame);
+ }
}
} else
#endif
@@ -1425,8 +1430,7 @@ void Wayland_HideWindow(_THIS, SDL_Window *window)
#ifdef HAVE_LIBDECOR_H
if (WINDOW_IS_LIBDECOR(data, window)) {
if (wind->shell_surface.libdecor.frame) {
- libdecor_frame_unref(wind->shell_surface.libdecor.frame);
- wind->shell_surface.libdecor.frame = NULL;
+ libdecor_frame_set_visibility(wind->shell_surface.libdecor.frame, false);
}
} else
#endif