diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 14aad1a1..a46ac469 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -286,7 +286,21 @@ void view_destroy(struct sway_view *view); void view_begin_destroy(struct sway_view *view); /** - * Map a view, ie. make it visible in the tree. + * Perform post-map setup like updating focus. view_setup must be called before + * calling view_map. + */ +void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); + +/** + * Prepare the view for its upcoming mapping, sending the intended dimensions + * so that the first frame has a chance of being correct. If CSD preferences or + * floating tendency changes, this may turn out to be inaccurate but no worse + * than skipping the step. + * + * This may bail early for some views if the surface is not mapped, in which + * case it should be called again before view_map once the surface is mapped. + * It is safe to call view_setup adancegain even if the first call before + * surface map succeeded. * * `fullscreen` should be set to true (and optionally `fullscreen_output` * should be populated) if the view should be made fullscreen immediately. @@ -294,7 +308,7 @@ void view_begin_destroy(struct sway_view *view); * `decoration` should be set to true if the client prefers CSD. The client's * preference may be ignored. */ -void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, +void view_setup(struct sway_view *view, struct wlr_surface *wlr_surface, bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); void view_unmap(struct sway_view *view); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3aed4ec7..44f584f2 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -277,6 +277,20 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; +static bool view_wants_csd(struct sway_view *view) { + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; + struct wlr_xdg_surface *xdg_surface = toplevel->base; + if (view->xdg_decoration) { + enum wlr_xdg_toplevel_decoration_v1_mode mode = + view->xdg_decoration->wlr_xdg_decoration->requested_mode; + return mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + } + struct sway_server_decoration *deco = + decoration_from_surface(xdg_surface->surface); + return !deco || deco->wlr_server_decoration->mode == + WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; +} + static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit); @@ -287,10 +301,14 @@ static void handle_commit(struct wl_listener *listener, void *data) { if (view->xdg_decoration != NULL) { set_xdg_decoration_mode(view->xdg_decoration); } - // XXX: https://github.com/swaywm/sway/issues/2176 + wlr_xdg_surface_schedule_configure(xdg_surface); wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel, XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); + view_setup(&xdg_shell_view->view, xdg_surface->surface, false, NULL, + view_wants_csd(&xdg_shell_view->view)); + transaction_commit_dirty(); + // TODO: wlr_xdg_toplevel_set_bounds() return; } @@ -465,23 +483,8 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = toplevel->base->geometry.width; view->natural_height = toplevel->base->geometry.height; - bool csd = false; - - if (view->xdg_decoration) { - enum wlr_xdg_toplevel_decoration_v1_mode mode = - view->xdg_decoration->wlr_xdg_decoration->requested_mode; - csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; - } else { - struct sway_server_decoration *deco = - decoration_from_surface(toplevel->base->surface); - csd = !deco || deco->wlr_server_decoration->mode == - WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; - } - - view_map(view, toplevel->base->surface, - toplevel->requested.fullscreen, - toplevel->requested.fullscreen_output, - csd); + view_setup(view, toplevel->base->surface, false, NULL, view_wants_csd(view)); + view_map(view, toplevel->base->surface); transaction_commit_dirty(); @@ -518,7 +521,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy); struct sway_view *view = &xdg_shell_view->view; - if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { + if (!sway_assert(view->surface == NULL || + !view->surface->mapped, "Tried to destroy a mapped view")) { return; } wl_list_remove(&xdg_shell_view->destroy.link); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b83537a0..b634d97e 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -526,7 +526,8 @@ static void handle_map(struct wl_listener *listener, void *data) { xwayland_view->commit.notify = handle_commit; // Put it back into the tree - view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); + view_setup(view, xsurface->surface, xsurface->fullscreen, NULL, false); + view_map(view, xsurface->surface); xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( xwayland_view->view.content_tree, xsurface->surface); diff --git a/sway/tree/view.c b/sway/tree/view.c index d25a09c2..9332e501 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -714,12 +714,23 @@ static void handle_foreign_destroy( wl_list_remove(&view->foreign_destroy.link); } -void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, +void view_setup(struct sway_view *view, struct wlr_surface *wlr_surface, bool fullscreen, struct wlr_output *fullscreen_output, bool decoration) { - if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { + if (view->surface) { + // Setup has already already completed return; } + if (!wlr_surface->mapped && view->impl->wants_floating && + view->impl->wants_floating(view)) { + // Configuring floating containers before the surface is mapped lead to + // us accidentally sending the configured minimum dimensions, which we + // only want to send if the client comes up with unreasonably small + // dimensions. As a hack, just skip this scenario for now and have the + // caller call view_setup again after the surface has been mapped. + return; + } + view->surface = wlr_surface; view_populate_pid(view); view->container = container_create(view); @@ -846,6 +857,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, view_execute_criteria(view); + const char *app_id; + const char *class; + if ((app_id = view_get_app_id(view)) != NULL) { + wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); + } else if ((class = view_get_class(view)) != NULL) { + wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class); + } + + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); + } +} + +void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { + if (!sway_assert(view->surface != NULL, "cannot map view that has not been setup")) { + return; + } + bool set_focus = should_focus(view); #if WLR_HAS_XWAYLAND @@ -859,18 +888,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, if (set_focus) { input_manager_set_focus(&view->container->node); } - - if (view->ext_foreign_toplevel) { - update_ext_foreign_toplevel(view); - } - - const char *app_id; - const char *class; - if ((app_id = view_get_app_id(view)) != NULL) { - wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); - } else if ((class = view_get_class(view)) != NULL) { - wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class); - } } void view_unmap(struct sway_view *view) {