From 188811f80861caacd016b857b0d07f6d2d62d15a Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 18 Jan 2024 10:04:51 -0500 Subject: [PATCH] scene_graph: Port layer_shell --- include/sway/layers.h | 44 +- include/sway/output.h | 19 +- include/sway/scene_descriptor.h | 1 + include/sway/tree/root.h | 5 + sway/desktop/layer_shell.c | 720 ++++++++++---------------------- sway/desktop/output.c | 85 ---- sway/input/cursor.c | 6 + sway/tree/output.c | 13 +- sway/tree/root.c | 5 + 9 files changed, 256 insertions(+), 642 deletions(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index 9220bdb5..a7afb900 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -4,53 +4,30 @@ #include #include -enum layer_parent { - LAYER_PARENT_LAYER, - LAYER_PARENT_POPUP, -}; - struct sway_layer_surface { - struct wlr_layer_surface_v1 *layer_surface; - struct wl_list link; - - struct wl_listener destroy; struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; struct wl_listener output_destroy; + struct wl_listener node_destroy; struct wl_listener new_popup; - struct wl_listener new_subsurface; - struct wlr_box geo; bool mapped; - struct wlr_box extent; - enum zwlr_layer_shell_v1_layer layer; - struct wl_list subsurfaces; + struct sway_output *output; + struct wlr_scene_layer_surface_v1 *scene; + struct wlr_scene_tree *tree; + struct wlr_scene_tree *popups; + struct wlr_layer_surface_v1 *layer_surface; }; struct sway_layer_popup { struct wlr_xdg_popup *wlr_popup; - enum layer_parent parent_type; - union { - struct sway_layer_surface *parent_layer; - struct sway_layer_popup *parent_popup; - }; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; - struct wl_listener commit; - struct wl_listener new_popup; -}; - -struct sway_layer_subsurface { - struct wlr_subsurface *wlr_subsurface; - struct sway_layer_surface *layer_surface; - struct wl_list link; + struct wlr_scene_tree *scene; + struct sway_layer_surface *toplevel; - struct wl_listener map; - struct wl_listener unmap; struct wl_listener destroy; + struct wl_listener new_popup; struct wl_listener commit; }; @@ -61,7 +38,4 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( void arrange_layers(struct sway_output *output); -struct sway_layer_surface *layer_from_wlr_layer_surface_v1( - struct wlr_layer_surface_v1 *layer_surface); - #endif diff --git a/include/sway/output.h b/include/sway/output.h index d353ce61..ea5d8f47 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -22,8 +22,12 @@ struct sway_output { struct sway_node node; struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; struct wlr_scene_tree *tiling; struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *shell_top; + struct wlr_scene_tree *shell_overlay; struct wlr_scene_tree *session_lock; } layers; @@ -39,7 +43,6 @@ struct sway_output { struct sway_server *server; struct wl_list link; - struct wl_list shell_layers[4]; // sway_layer_surface::link struct wlr_box usable_area; struct wlr_damage_ring damage_ring; @@ -124,8 +127,6 @@ void output_enable(struct sway_output *output); void output_disable(struct sway_output *output); -bool output_has_opaque_overlay_layer_surface(struct sway_output *output); - struct sway_workspace *output_get_active_workspace(struct sway_output *output); void output_surface_for_each_surface(struct sway_output *output, @@ -140,18 +141,6 @@ void output_view_for_each_popup_surface(struct sway_output *output, struct sway_view *view, sway_surface_iterator_func_t iterator, void *user_data); -void output_layer_for_each_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_toplevel_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_popup_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - #if HAVE_XWAYLAND void output_unmanaged_for_each_surface(struct sway_output *output, struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 8af81219..970adaa5 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -15,6 +15,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_NON_INTERACTIVE, SWAY_SCENE_DESC_CONTAINER, SWAY_SCENE_DESC_VIEW, + SWAY_SCENE_DESC_LAYER_SHELL, SWAY_SCENE_DESC_DRAG_ICON, }; diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 003606aa..2f717bae 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -40,10 +40,15 @@ struct sway_root { struct wlr_scene_tree *layer_tree; struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; struct wlr_scene_tree *tiling; struct wlr_scene_tree *floating; + struct wlr_scene_tree *shell_top; struct wlr_scene_tree *fullscreen; struct wlr_scene_tree *fullscreen_global; + struct wlr_scene_tree *shell_overlay; + struct wlr_scene_tree *popup; struct wlr_scene_tree *seat; struct wlr_scene_tree *session_lock; } layers; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 6480d7ce..a52d27fa 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -6,6 +6,7 @@ #include #include #include "log.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -16,6 +17,7 @@ #include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" +#include struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( struct wlr_surface *surface) { @@ -50,165 +52,22 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( } while (true); } -static void apply_exclusive(struct wlr_box *usable_area, - uint32_t anchor, int32_t exclusive, - int32_t margin_top, int32_t margin_right, - int32_t margin_bottom, int32_t margin_left) { - if (exclusive <= 0) { - return; - } - struct { - uint32_t singular_anchor; - uint32_t anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; - } edges[] = { - // Top - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .positive_axis = &usable_area->y, - .negative_axis = &usable_area->height, - .margin = margin_top, - }, - // Bottom - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->height, - .margin = margin_bottom, - }, - // Left - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = &usable_area->x, - .negative_axis = &usable_area->width, - .margin = margin_left, - }, - // Right - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->width, - .margin = margin_right, - }, - }; - for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { - if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) - && exclusive + edges[i].margin > 0) { - if (edges[i].positive_axis) { - *edges[i].positive_axis += exclusive + edges[i].margin; - } - if (edges[i].negative_axis) { - *edges[i].negative_axis -= exclusive + edges[i].margin; - } - break; +static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, + struct wlr_box *usable_area, struct wlr_scene_tree *tree) { + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + // surface could be null during destruction + if (!surface) { + continue; } - } -} -static void arrange_layer(struct sway_output *output, struct wl_list *list, - struct wlr_box *usable_area, bool exclusive) { - struct sway_layer_surface *sway_layer; - struct wlr_box full_area = { 0 }; - wlr_output_effective_resolution(output->wlr_output, - &full_area.width, &full_area.height); - wl_list_for_each(sway_layer, list, link) { - struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; - if (!layer->initialized) { + if (!surface->scene->layer_surface->initialized) { return; } - struct wlr_layer_surface_v1_state *state = &layer->current; - if (exclusive != (state->exclusive_zone > 0)) { - continue; - } - struct wlr_box bounds; - if (state->exclusive_zone == -1) { - bounds = full_area; - } else { - bounds = *usable_area; - } - struct wlr_box box = { - .width = state->desired_width, - .height = state->desired_height - }; - // Horizontal axis - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if (box.width == 0) { - box.x = bounds.x; - } else if ((state->anchor & both_horiz) == both_horiz) { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x = bounds.x; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } - // Vertical axis - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (box.height == 0) { - box.y = bounds.y; - } else if ((state->anchor & both_vert) == both_vert) { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y = bounds.y; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } - // Margin - if (box.width == 0) { - box.x += state->margin.left; - box.width = bounds.width - - (state->margin.left + state->margin.right); - } else if ((state->anchor & both_horiz) == both_horiz) { - // don't apply margins - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x += state->margin.left; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x -= state->margin.right; - } - if (box.height == 0) { - box.y += state->margin.top; - box.height = bounds.height - - (state->margin.top + state->margin.bottom); - } else if ((state->anchor & both_vert) == both_vert) { - // don't apply margins - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y += state->margin.top; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y -= state->margin.bottom; - } - if (!sway_assert(box.width >= 0 && box.height >= 0, - "Expected layer surface to have positive size")) { - continue; - } - // Apply - sway_layer->geo = box; - apply_exclusive(usable_area, state->anchor, state->exclusive_zone, - state->margin.top, state->margin.right, - state->margin.bottom, state->margin.left); - wlr_layer_surface_v1_configure(layer, box.width, box.height); + + wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); } } @@ -216,83 +75,78 @@ void arrange_layers(struct sway_output *output) { struct wlr_box usable_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, &usable_area.width, &usable_area.height); + const struct wlr_box full_area = usable_area; - // Arrange exclusive surfaces from top->bottom - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, true); - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, true); - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, true); - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, true); - - if (memcmp(&usable_area, &output->usable_area, - sizeof(struct wlr_box)) != 0) { + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); + + if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); - memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + output->usable_area = usable_area; arrange_output(output); } +} - // Arrange non-exclusive surfaces from top->bottom - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, false); - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, false); - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, false); - arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, false); - - // Find topmost keyboard interactive layer, if such a layer exists - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); - struct sway_layer_surface *layer, *topmost = NULL; - for (size_t i = 0; i < nlayers; ++i) { - wl_list_for_each_reverse(layer, - &output->shell_layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive && - layer->layer_surface->surface->mapped) { - topmost = layer; - break; - } - } - if (topmost != NULL) { - break; - } +static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, + enum zwlr_layer_shell_v1_layer type) { + switch (type) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + return output->layers.shell_background; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + return output->layers.shell_bottom; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + return output->layers.shell_top; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + return output->layers.shell_overlay; } - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat->has_exclusive_layer = false; - if (topmost != NULL) { - seat_set_focus_layer(seat, topmost->layer_surface); - } else if (seat->focused_layer && - seat->focused_layer->current.keyboard_interactive - != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { - seat_set_focus_layer(seat, NULL); - } + sway_assert(false, "unreachable"); + return NULL; +} + +static struct sway_layer_surface *sway_layer_surface_create( + struct wlr_scene_layer_surface_v1 *scene) { + struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); + if (!surface) { + sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); + return NULL; } + + struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); + if (!popups) { + sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); + free(surface); + return NULL; + } + + surface->tree = scene->tree; + surface->scene = scene; + surface->layer_surface = scene->layer_surface; + surface->popups = popups; + + return surface; } static struct sway_layer_surface *find_mapped_layer_by_client( - struct wl_client *client, struct wlr_output *ignore_output) { + struct wl_client *client, struct sway_output *ignore_output) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - if (output->wlr_output == ignore_output) { + if (output == ignore_output) { continue; } // For now we'll only check the overlay layer - struct sway_layer_surface *lsurface; - wl_list_for_each(lsurface, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { - struct wl_resource *resource = lsurface->layer_surface->resource; + struct wlr_scene_node *node; + wl_list_for_each (node, &output->layers.shell_overlay->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + struct wl_resource *resource = layer_surface->resource; if (wl_resource_get_client(resource) == client - && lsurface->layer_surface->surface->mapped) { - return lsurface; + && layer_surface->surface->mapped) { + return surface; } } } @@ -300,262 +154,144 @@ static struct sway_layer_surface *find_mapped_layer_by_client( } static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = - wl_container_of(listener, sway_layer, output_destroy); + struct sway_layer_surface *layer = + wl_container_of(listener, layer, output_destroy); + + layer->output = NULL; + wlr_scene_node_destroy(&layer->scene->tree->node); +} + +static void handle_node_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *layer = + wl_container_of(listener, layer, node_destroy); + + // destroy the scene descriptor straight away if it exists, otherwise + // we will try to reflow still considering the destroyed node. + scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); + // Determine if this layer is being used by an exclusive client. If it is, // try and find another layer owned by this client to pass focus to. struct sway_seat *seat = input_manager_get_default_seat(); struct wl_client *client = - wl_resource_get_client(sway_layer->layer_surface->resource); - - if (!server.session_lock.locked) { - struct sway_layer_surface *layer = - find_mapped_layer_by_client(client, sway_layer->layer_surface->output); - if (layer) { - seat_set_focus_layer(seat, layer->layer_surface); + wl_resource_get_client(layer->layer_surface->resource); + if (!server.session_lock.lock) { + struct sway_layer_surface *consider_layer = + find_mapped_layer_by_client(client, layer->output); + if (consider_layer) { + seat_set_focus_layer(seat, consider_layer->layer_surface); } } - wlr_layer_surface_v1_destroy(sway_layer->layer_surface); + if (layer->output) { + arrange_layers(layer->output); + transaction_commit_dirty(); + } + + wlr_scene_node_destroy(&layer->popups->node); + + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->surface_commit.link); + wl_list_remove(&layer->node_destroy.link); + wl_list_remove(&layer->output_destroy.link); + + free(layer); } static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_layer_surface *layer = - wl_container_of(listener, layer, surface_commit); - struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; - struct wlr_output *wlr_output = layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - - if (layer_surface->initial_commit) { - surface_enter_output(layer_surface->surface, output); - } else if (!layer_surface->initialized) { + struct sway_layer_surface *surface = + wl_container_of(listener, surface, surface_commit); + + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + if (!layer_surface->initialized) { return; } - struct wlr_box old_extent = layer->extent; - - bool layer_changed = false; - if (layer_surface->initial_commit || layer_surface->current.committed != 0 - || layer->mapped != layer_surface->surface->mapped) { - layer->mapped = layer_surface->surface->mapped; - layer_changed = layer->layer != layer_surface->current.layer; - if (layer_changed) { - wl_list_remove(&layer->link); - wl_list_insert(&output->shell_layers[layer_surface->current.layer], - &layer->link); - layer->layer = layer_surface->current.layer; - } - arrange_layers(output); + uint32_t committed = layer_surface->current.committed; + if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; + struct wlr_scene_tree *output_layer = sway_layer_get_scene( + surface->output, layer_type); + wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); } - wlr_surface_get_extends(layer_surface->surface, &layer->extent); - layer->extent.x += layer->geo.x; - layer->extent.y += layer->geo.y; - - bool extent_changed = - memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; - if (extent_changed || layer_changed) { - old_extent.x += output->lx; - old_extent.y += output->ly; - output_damage_box(output, &old_extent); - output_damage_surface(output, layer->geo.x, layer->geo.y, - layer_surface->surface, true); - } else { - output_damage_surface(output, layer->geo.x, layer->geo.y, - layer_surface->surface, false); + if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { + surface->mapped = layer_surface->surface->mapped; + arrange_layers(surface->output); + transaction_commit_dirty(); } - transaction_commit_dirty(); + int lx, ly; + wlr_scene_node_coords(&surface->scene->tree->node, &lx, &ly); + wlr_scene_node_set_position(&surface->popups->node, lx, ly); } -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); - -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = - wl_container_of(listener, sway_layer, destroy); - sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", - sway_layer->layer_surface->namespace); - - struct sway_layer_subsurface *subsurface, *subsurface_tmp; - wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { - layer_subsurface_destroy(subsurface); +static void handle_map(struct wl_listener *listener, void *data) { + struct sway_layer_surface *surface = wl_container_of(listener, + surface, map); + + struct wlr_layer_surface_v1 *layer_surface = + surface->scene->layer_surface; + + // focus on new surface + if (layer_surface->current.keyboard_interactive && + (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || + layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + // but only if the currently focused layer has a lower precedence + if (!seat->focused_layer || + seat->focused_layer->current.layer >= layer_surface->current.layer) { + seat_set_focus_layer(seat, layer_surface); + } + } + arrange_layers(surface->output); } - wl_list_remove(&sway_layer->link); - wl_list_remove(&sway_layer->destroy.link); - wl_list_remove(&sway_layer->map.link); - wl_list_remove(&sway_layer->unmap.link); - wl_list_remove(&sway_layer->surface_commit.link); - wl_list_remove(&sway_layer->new_popup.link); - wl_list_remove(&sway_layer->new_subsurface.link); - - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - arrange_layers(output); - transaction_commit_dirty(); - wl_list_remove(&sway_layer->output_destroy.link); - sway_layer->layer_surface->output = NULL; - - free(sway_layer); -} - -static void handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = wl_container_of(listener, - sway_layer, map); - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, - sway_layer->layer_surface->surface, true); cursor_rebase_all(); } static void handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = wl_container_of( - listener, sway_layer, unmap); + struct sway_layer_surface *surface = wl_container_of( + listener, surface, unmap); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - if (seat->focused_layer == sway_layer->layer_surface) { + if (seat->focused_layer == surface->layer_surface) { seat_set_focus_layer(seat, NULL); } } cursor_rebase_all(); - - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, - sway_layer->layer_surface->surface, true); -} - -static void subsurface_damage(struct sway_layer_subsurface *subsurface, - bool whole) { - struct sway_layer_surface *layer = subsurface->layer_surface; - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; - int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; - output_damage_surface( - output, ox, oy, subsurface->wlr_subsurface->surface, whole); } -static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, unmap); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, map); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, commit); - subsurface_damage(subsurface, false); -} - -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { - wl_list_remove(&subsurface->link); - wl_list_remove(&subsurface->map.link); - wl_list_remove(&subsurface->unmap.link); - wl_list_remove(&subsurface->destroy.link); - wl_list_remove(&subsurface->commit.link); - free(subsurface); -} - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - layer_subsurface_destroy(subsurface); -} - -static struct sway_layer_subsurface *create_subsurface( - struct wlr_subsurface *wlr_subsurface, - struct sway_layer_surface *layer_surface) { - struct sway_layer_subsurface *subsurface = - calloc(1, sizeof(struct sway_layer_subsurface)); - if (subsurface == NULL) { - return NULL; - } - - subsurface->wlr_subsurface = wlr_subsurface; - subsurface->layer_surface = layer_surface; - wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); - - subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->surface->events.map, &subsurface->map); - subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->surface->events.unmap, &subsurface->unmap); - subsurface->destroy.notify = subsurface_handle_destroy; - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->commit.notify = subsurface_handle_commit; - wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); - - return subsurface; -} - -static void handle_new_subsurface(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer_surface = - wl_container_of(listener, sway_layer_surface, new_subsurface); - struct wlr_subsurface *wlr_subsurface = data; - create_subsurface(wlr_subsurface, sway_layer_surface); -} - - -static struct sway_layer_surface *popup_get_layer( - struct sway_layer_popup *popup) { - while (popup->parent_type == LAYER_PARENT_POPUP) { - popup = popup->parent_popup; - } - return popup->parent_layer; -} +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_popup *popup = + wl_container_of(listener, popup, destroy); -static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { - struct wlr_xdg_popup *popup = layer_popup->wlr_popup; - struct wlr_surface *surface = popup->base->surface; - int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; - int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; - int ox = popup_sx, oy = popup_sy; - struct sway_layer_surface *layer; - while (true) { - if (layer_popup->parent_type == LAYER_PARENT_POPUP) { - layer_popup = layer_popup->parent_popup; - ox += layer_popup->wlr_popup->current.geometry.x; - oy += layer_popup->wlr_popup->current.geometry.y; - } else { - layer = layer_popup->parent_layer; - ox += layer->geo.x; - oy += layer->geo.y; - break; - } - } - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, ox, oy, surface, whole); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + wl_list_remove(&popup->commit.link); + free(popup); } static void popup_unconstrain(struct sway_layer_popup *popup) { - struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + struct sway_output *output = popup->toplevel->output; + + // if a client tries to create a popup while we are in the process of destroying + // its output, don't crash. + if (!output) { + return; + } - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; + int lx, ly; + wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = -layer->geo.x, - .y = -layer->geo.y, + .x = output->lx - lx, + .y = output->ly - ly, .width = output->width, .height = output->height, }; @@ -563,63 +299,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); } -static void popup_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, map); - struct sway_layer_surface *layer = popup_get_layer(popup); - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - surface_enter_output(popup->wlr_popup->base->surface, wlr_output->data); - popup_damage(popup, true); -} - -static void popup_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); - popup_damage(popup, true); -} - static void popup_handle_commit(struct wl_listener *listener, void *data) { struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); if (popup->wlr_popup->base->initial_commit) { popup_unconstrain(popup); } - popup_damage(popup, false); -} - -static void popup_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = - wl_container_of(listener, popup, destroy); - - wl_list_remove(&popup->map.link); - wl_list_remove(&popup->unmap.link); - wl_list_remove(&popup->destroy.link); - wl_list_remove(&popup->commit.link); - free(popup); } static void popup_handle_new_popup(struct wl_listener *listener, void *data); static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, - enum layer_parent parent_type, void *parent) { - struct sway_layer_popup *popup = - calloc(1, sizeof(struct sway_layer_popup)); + struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { + struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); if (popup == NULL) { return NULL; } + popup->toplevel = toplevel; popup->wlr_popup = wlr_popup; - popup->parent_type = parent_type; - popup->parent_layer = parent; + popup->scene = wlr_scene_xdg_surface_create(parent, + wlr_popup->base); + + if (!popup->scene) { + free(popup); + return NULL; + } - popup->map.notify = popup_handle_map; - wl_signal_add(&wlr_popup->base->surface->events.map, &popup->map); - popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->surface->events.unmap, &popup->unmap); popup->destroy.notify = popup_handle_destroy; wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); - popup->commit.notify = popup_handle_commit; - wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + popup->commit.notify = popup_handle_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); return popup; } @@ -628,19 +339,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_layer_popup *sway_layer_popup = wl_container_of(listener, sway_layer_popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); + create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); } static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer_surface = wl_container_of(listener, sway_layer_surface, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); -} - -struct sway_layer_surface *layer_from_wlr_layer_surface_v1( - struct wlr_layer_surface_v1 *layer_surface) { - return layer_surface->data; + create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); } void handle_layer_shell_surface(struct wl_listener *listener, void *data) { @@ -672,10 +378,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", layer_surface->namespace); - // Note that layer_surface->output can be NULL - // here, but none of our destroy callbacks are - // registered yet so we don't have to make them - // handle that case. wlr_layer_surface_v1_destroy(layer_surface); return; } @@ -684,37 +386,51 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->output = output->wlr_output; } - struct sway_layer_surface *sway_layer = - calloc(1, sizeof(struct sway_layer_surface)); - if (!sway_layer) { + struct sway_output *output = layer_surface->output->data; + + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; + struct wlr_scene_tree *output_layer = sway_layer_get_scene( + output, layer_type); + struct wlr_scene_layer_surface_v1 *scene_surface = + wlr_scene_layer_surface_v1_create(output_layer, layer_surface); + if (!scene_surface) { + sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); return; } - wl_list_init(&sway_layer->subsurfaces); + struct sway_layer_surface *surface = + sway_layer_surface_create(scene_surface); + if (!surface) { + wlr_layer_surface_v1_destroy(layer_surface); - sway_layer->surface_commit.notify = handle_surface_commit; - wl_signal_add(&layer_surface->surface->events.commit, - &sway_layer->surface_commit); - - sway_layer->destroy.notify = handle_destroy; - wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); - sway_layer->map.notify = handle_map; - wl_signal_add(&layer_surface->surface->events.map, &sway_layer->map); - sway_layer->unmap.notify = handle_unmap; - wl_signal_add(&layer_surface->surface->events.unmap, &sway_layer->unmap); - sway_layer->new_popup.notify = handle_new_popup; - wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); - sway_layer->new_subsurface.notify = handle_new_subsurface; - wl_signal_add(&layer_surface->surface->events.new_subsurface, - &sway_layer->new_subsurface); - - sway_layer->layer_surface = layer_surface; - layer_surface->data = sway_layer; + sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); + return; + } - struct sway_output *output = layer_surface->output->data; - sway_layer->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->events.disable, &sway_layer->output_destroy); + if (!scene_descriptor_assign(&scene_surface->tree->node, + SWAY_SCENE_DESC_LAYER_SHELL, surface)) { + sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); + // destroying the layer_surface will also destroy its corresponding + // scene node + wlr_layer_surface_v1_destroy(layer_surface); + return; + } - wl_list_insert(&output->shell_layers[layer_surface->pending.layer], - &sway_layer->link); + surface->output = output; + + surface->surface_commit.notify = handle_surface_commit; + wl_signal_add(&layer_surface->surface->events.commit, + &surface->surface_commit); + surface->map.notify = handle_map; + wl_signal_add(&layer_surface->surface->events.map, &surface->map); + surface->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); + surface->new_popup.notify = handle_new_popup; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); + + surface->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.disable, &surface->output_destroy); + + surface->node_destroy.notify = handle_node_destroy; + wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1e4474ce..942bc780 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -182,66 +182,6 @@ void output_view_for_each_popup_surface(struct sway_output *output, view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); } -void output_layer_for_each_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -void output_layer_for_each_toplevel_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, - layer_surface->geo.x, layer_surface->geo.y, iterator, - user_data); - } -} - - -void output_layer_for_each_popup_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - #if HAVE_XWAYLAND void output_unmanaged_for_each_surface(struct sway_output *output, struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, @@ -282,31 +222,6 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) { return focus->sway_workspace; } -bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { - struct sway_layer_surface *sway_layer_surface; - wl_list_for_each(sway_layer_surface, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { - struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; - pixman_box32_t output_box = { - .x2 = output->width, - .y2 = output->height, - }; - pixman_region32_t surface_opaque_box; - pixman_region32_init(&surface_opaque_box); - pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); - pixman_region32_translate(&surface_opaque_box, - sway_layer_surface->geo.x, sway_layer_surface->geo.y); - pixman_region_overlap_t contains = - pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); - pixman_region32_fini(&surface_opaque_box); - - if (contains == PIXMAN_REGION_IN) { - return true; - } - } - return false; -} - struct send_frame_done_data { struct timespec when; int msec_until_refresh; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 79373e40..30df76f4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -94,6 +94,12 @@ struct sway_node *node_at_coords( } } + if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { + // We don't want to feed through the current workspace on + // layer shells + return NULL; + } + if (!current->parent) { break; } diff --git a/sway/tree/output.c b/sway/tree/output.c index 64ca3d75..3c8823dc 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -93,8 +93,12 @@ static void destroy_scene_layers(struct sway_output *output) { scene_node_disown_children(output->layers.tiling); scene_node_disown_children(output->layers.fullscreen); + wlr_scene_node_destroy(&output->layers.shell_background->node); + wlr_scene_node_destroy(&output->layers.shell_bottom->node); wlr_scene_node_destroy(&output->layers.tiling->node); wlr_scene_node_destroy(&output->layers.fullscreen->node); + wlr_scene_node_destroy(&output->layers.shell_top->node); + wlr_scene_node_destroy(&output->layers.shell_overlay->node); wlr_scene_node_destroy(&output->layers.session_lock->node); } @@ -103,8 +107,12 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { node_init(&output->node, N_OUTPUT, output); bool failed = false; + output->layers.shell_background = alloc_scene_tree(root->staging, &failed); + output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed); output->layers.tiling = alloc_scene_tree(root->staging, &failed); output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); + output->layers.shell_top = alloc_scene_tree(root->staging, &failed); + output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed); output->layers.session_lock = alloc_scene_tree(root->staging, &failed); if (!failed) { @@ -136,11 +144,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { output->workspaces = create_list(); output->current.workspaces = create_list(); - size_t len = sizeof(output->shell_layers) / sizeof(output->shell_layers[0]); - for (size_t i = 0; i < len; ++i) { - wl_list_init(&output->shell_layers[i]); - } - return output; } diff --git a/sway/tree/root.c b/sway/tree/root.c index fbdd9a96..7c8f9ea6 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -47,10 +47,15 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->staging = alloc_scene_tree(&root_scene->tree, &failed); root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed); + root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed); root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); + root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); + root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); + root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);