diff --git a/view.c b/view.c index 1050e26..a5a81e5 100644 --- a/view.c +++ b/view.c @@ -20,6 +20,9 @@ #include "seat.h" #include "server.h" #include "view.h" +#if CAGE_HAS_XWAYLAND +#include "xwayland.h" +#endif char * view_get_title(struct cg_view *view) @@ -115,6 +118,14 @@ void view_destroy(struct cg_view *view) { struct cg_server *server = view->server; + bool ever_been_mapped = true; + +#if CAGE_HAS_XWAYLAND + if (view->type == CAGE_XWAYLAND_VIEW) { + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + ever_been_mapped = xwayland_view->ever_been_mapped; + } +#endif if (view->wlr_surface != NULL) { view_unmap(view); @@ -127,7 +138,7 @@ view_destroy(struct cg_view *view) if (!empty) { struct cg_view *prev = wl_container_of(server->views.next, prev, link); seat_set_focus(server->seat, prev); - } else { + } else if (ever_been_mapped) { /* The list is empty and the last view has been mapped, so we can safely exit. */ wl_display_terminate(server->wl_display); diff --git a/xwayland.c b/xwayland.c index 2e3fe00..b8317c9 100644 --- a/xwayland.c +++ b/xwayland.c @@ -106,6 +106,7 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data) struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, map); struct cg_view *view = &xwayland_view->view; + xwayland_view->ever_been_mapped = true; view_map(view, xwayland_view->xwayland_surface->surface); } diff --git a/xwayland.h b/xwayland.h index e8ec9f9..b00230c 100644 --- a/xwayland.h +++ b/xwayland.h @@ -10,6 +10,20 @@ struct cg_xwayland_view { struct cg_view view; struct wlr_xwayland_surface *xwayland_surface; + /* Some applications that aren't yet Wayland-native or + otherwise "special" (e.g. Firefox Nightly and Google + Chrome/Chromium) spawn an XWayland surface upon startup + that is almost immediately closed again. This makes Cage + think there are no views left, which results in it + exiting. However, after this initial (unmapped) surface, + the "real" application surface is opened. This leads to + these applications' startup sequences being interrupted by + Cage exiting. Hence, to work around this issue, Cage checks + whether an XWayland surface has ever been mapped and exits + only if 1) the XWayland surface has ever been mapped and 2) + this was the last surface Cage manages. */ + bool ever_been_mapped; + struct wl_listener destroy; struct wl_listener unmap; struct wl_listener map;