From 904d256581c4d2f6b2b08cd0ef84966bb8a0befd Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 13 Jan 2024 17:04:22 +0300 Subject: [PATCH 01/36] layer-shell: don't try to unmap on destroy A surface is guaranteed to be unmapped on destruction. --- sway/desktop/layer_shell.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 31d8558cc..1da4c61a4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -360,23 +360,6 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } -static void unmap(struct sway_layer_surface *sway_layer) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (seat->focused_layer == sway_layer->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 layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); static void handle_destroy(struct wl_listener *listener, void *data) { @@ -384,9 +367,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, sway_layer, destroy); sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", sway_layer->layer_surface->namespace); - if (sway_layer->layer_surface->surface->mapped) { - unmap(sway_layer); - } struct sway_layer_subsurface *subsurface, *subsurface_tmp; wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { @@ -426,7 +406,20 @@ static void handle_map(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of( listener, sway_layer, unmap); - unmap(sway_layer); + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + if (seat->focused_layer == sway_layer->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, From 8d1b0cecd9cf4ef4254c9453c7d6d9649d0c7717 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 17 Jan 2024 18:50:47 +0300 Subject: [PATCH 02/36] layer-shell: wait for an initial commit before configuring --- sway/desktop/layer_shell.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 1da4c61a4..d493fb39c 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -324,10 +324,15 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { 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); + } + struct wlr_box old_extent = layer->extent; bool layer_changed = false; - if (layer_surface->current.committed != 0 + 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; @@ -708,13 +713,4 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { wl_list_insert(&output->layers[layer_surface->pending.layer], &sway_layer->link); - - surface_enter_output(layer_surface->surface, output); - - // Temporarily set the layer's current state to pending - // So that we can easily arrange it - struct wlr_layer_surface_v1_state old_state = layer_surface->current; - layer_surface->current = layer_surface->pending; - arrange_layers(output); - layer_surface->current = old_state; } From 2c69e19fd30c5ceac61d6af821c6c6875cd3f8b6 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 17 Jan 2024 18:51:21 +0300 Subject: [PATCH 03/36] layer-shell: don't configure uninitialized surfaces --- sway/desktop/layer_shell.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d493fb39c..e5e7046a3 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -131,6 +131,9 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list, &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) { + return; + } struct wlr_layer_surface_v1_state *state = &layer->current; if (exclusive != (state->exclusive_zone > 0)) { continue; @@ -327,6 +330,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { if (layer_surface->initial_commit) { surface_enter_output(layer_surface->surface, output); + } else if (!layer_surface->initialized) { + return; } struct wlr_box old_extent = layer->extent; From b4d7e84d3852ea93d2aab22f5993f84a7060b950 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 1 Mar 2022 16:19:23 -0500 Subject: [PATCH 04/36] desktop: Rename layers to shell_layers This code will be deleted later, but for the time being rename it so it doesn't conflict with future properties. --- include/sway/output.h | 2 +- sway/desktop/layer_shell.c | 27 +++++++++++++-------------- sway/desktop/output.c | 12 ++++++------ sway/desktop/render.c | 16 ++++++++-------- sway/input/cursor.c | 14 +++++++------- sway/tree/output.c | 4 ++-- 6 files changed, 37 insertions(+), 38 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 43cbccd26..96bd10db8 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -23,7 +23,7 @@ struct sway_output { struct sway_server *server; struct wl_list link; - struct wl_list layers[4]; // sway_layer_surface::link + struct wl_list shell_layers[4]; // sway_layer_surface::link struct wlr_box usable_area; struct timespec last_frame; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index e5e7046a3..6480d7ce4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -218,13 +218,13 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); // Arrange exclusive surfaces from top->bottom - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &usable_area, true); if (memcmp(&usable_area, &output->usable_area, @@ -235,13 +235,13 @@ void arrange_layers(struct sway_output *output) { } // Arrange non-exclusive surfaces from top->bottom - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + arrange_layer(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + 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 @@ -253,9 +253,8 @@ void arrange_layers(struct sway_output *output) { struct sway_layer_surface *layer, *topmost = NULL; for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layer, - &output->layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive - == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + &output->shell_layers[layers_above_shell[i]], link) { + if (layer->layer_surface->current.keyboard_interactive && layer->layer_surface->surface->mapped) { topmost = layer; break; @@ -289,7 +288,7 @@ static struct sway_layer_surface *find_mapped_layer_by_client( // For now we'll only check the overlay layer struct sway_layer_surface *lsurface; wl_list_for_each(lsurface, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { struct wl_resource *resource = lsurface->layer_surface->resource; if (wl_resource_get_client(resource) == client && lsurface->layer_surface->surface->mapped) { @@ -343,7 +342,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { layer_changed = layer->layer != layer_surface->current.layer; if (layer_changed) { wl_list_remove(&layer->link); - wl_list_insert(&output->layers[layer_surface->current.layer], + wl_list_insert(&output->shell_layers[layer_surface->current.layer], &layer->link); layer->layer = layer_surface->current.layer; } @@ -716,6 +715,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_layer->output_destroy.notify = handle_output_destroy; wl_signal_add(&output->events.disable, &sway_layer->output_destroy); - wl_list_insert(&output->layers[layer_surface->pending.layer], + wl_list_insert(&output->shell_layers[layer_surface->pending.layer], &sway_layer->link); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8b84da869..928c77d6d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -347,10 +347,10 @@ static void output_for_each_surface(struct sway_output *output, #endif } else { output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], iterator, user_data); output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], iterator, user_data); workspace_for_each_container(workspace, @@ -361,13 +361,13 @@ static void output_for_each_surface(struct sway_output *output, iterator, user_data); #endif output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], iterator, user_data); } overlay: output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], iterator, user_data); output_drag_icons_for_each_surface(output, &root->drag_icons, iterator, user_data); @@ -399,7 +399,7 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) { 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->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { + &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, @@ -488,7 +488,7 @@ static bool scan_out_fullscreen_view(struct sway_output *output, } #endif - if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { + if (!wl_list_empty(&output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { return false; } if (!wl_list_empty(&root->drag_icons)) { diff --git a/sway/desktop/render.c b/sway/desktop/render.c index c9a306cfe..168c941bd 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1098,9 +1098,9 @@ void output_render(struct render_context *ctx) { }); render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); render_workspace(ctx, workspace, workspace->current.focused); render_floating(ctx); @@ -1108,14 +1108,14 @@ void output_render(struct render_context *ctx) { render_unmanaged(ctx, &root->xwayland_unmanaged); #endif render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } render_seatops(ctx); @@ -1128,9 +1128,9 @@ void output_render(struct render_context *ctx) { render_overlay: render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_drag_icons(ctx, &root->drag_icons); renderer_end: diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 6b6faf640..73aef4b03 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -115,7 +115,7 @@ struct sway_node *node_at_coords( // layer surfaces on the overlay layer are rendered on top if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], ox, oy, sx, sy))) { return NULL; } @@ -176,22 +176,22 @@ struct sway_node *node_at_coords( return NULL; } if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], ox, oy, sx, sy))) { return NULL; } if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], ox, oy, sx, sy))) { return NULL; } if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], ox, oy, sx, sy))) { return NULL; } if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], ox, oy, sx, sy))) { return NULL; } @@ -202,12 +202,12 @@ struct sway_node *node_at_coords( } if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], ox, oy, sx, sy))) { return NULL; } if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], ox, oy, sx, sy))) { return NULL; } diff --git a/sway/tree/output.c b/sway/tree/output.c index 4aa3a7fea..2186ad0c6 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -102,9 +102,9 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { output->workspaces = create_list(); output->current.workspaces = create_list(); - size_t len = sizeof(output->layers) / sizeof(output->layers[0]); + size_t len = sizeof(output->shell_layers) / sizeof(output->shell_layers[0]); for (size_t i = 0; i < len; ++i) { - wl_list_init(&output->layers[i]); + wl_list_init(&output->shell_layers[i]); } return output; From dbd2fbf4301d441be4f9a04a1df6d93c81c361f6 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 4 Mar 2022 19:23:27 -0500 Subject: [PATCH 05/36] view: init function should return a success bool --- include/sway/tree/view.h | 2 +- sway/desktop/xdg_shell.c | 5 ++++- sway/desktop/xwayland.c | 5 ++++- sway/tree/view.c | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 856651a52..822e7bb33 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -311,7 +311,7 @@ void view_for_each_popup_surface(struct sway_view *view, // view implementation -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl); void view_destroy(struct sway_view *view); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 63a0835bf..11c112bee 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -548,7 +548,10 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { return; } - view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); + if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { + free(xdg_shell_view); + return; + } xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; xdg_shell_view->map.notify = handle_map; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 2c6184fde..8f79b5e79 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -796,7 +796,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu return NULL; } - view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); + if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { + free(xwayland_view); + return NULL; + } xwayland_view->view.wlr_xwayland_surface = xsurface; wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); diff --git a/sway/tree/view.c b/sway/tree/view.c index 00dc47215..d62a0667b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -35,7 +35,7 @@ #include "pango.h" #include "stringop.h" -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { view->type = type; view->impl = impl; @@ -44,6 +44,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; wl_signal_init(&view->events.unmap); + return true; } void view_destroy(struct sway_view *view) { From 1eb16d136774c8fb3c9085df45156264f0db8814 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 18 Jan 2024 10:00:45 -0500 Subject: [PATCH 06/36] scene_graph: Maintain `wlr_scene_node`s for the sway tree. --- include/sway/output.h | 15 +++++++ include/sway/tree/container.h | 21 ++++++++++ include/sway/tree/node.h | 12 ++++++ include/sway/tree/root.h | 26 ++++++++++++ include/sway/tree/view.h | 4 ++ include/sway/tree/workspace.h | 7 ++++ sway/desktop/output.c | 22 ++++++++--- sway/tree/container.c | 74 +++++++++++++++++++++++++++++++++-- sway/tree/node.c | 29 ++++++++++++++ sway/tree/output.c | 34 ++++++++++++++++ sway/tree/root.c | 28 +++++++++++++ sway/tree/view.c | 10 ++++- sway/tree/workspace.c | 17 ++++++++ 13 files changed, 290 insertions(+), 9 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 96bd10db8..691ac8ddd 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "config.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -19,7 +20,21 @@ struct sway_output_state { struct sway_output { struct sway_node node; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + } layers; + + // when a container is fullscreen, in case the fullscreen surface is + // translucent (can see behind) we must make sure that the background is a + // solid color in order to conform to the wayland protocol. This rect + // ensures that when looking through a surface, all that will be seen + // is black. + struct wlr_scene_rect *fullscreen_background; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; struct sway_server *server; struct wl_list link; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index fe3ee8a8b..ee22a0d02 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "list.h" #include "sway/tree/node.h" @@ -68,6 +69,26 @@ struct sway_container { struct sway_node node; struct sway_view *view; + struct wlr_scene_tree *scene_tree; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_tree *border; + struct wlr_scene_tree *background; + } title_bar; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *top; + struct wlr_scene_rect *bottom; + struct wlr_scene_rect *left; + struct wlr_scene_rect *right; + } border; + + struct wlr_scene_tree *content_tree; + struct sway_container_state current; struct sway_container_state pending; diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 03a389a48..e2dbcdf05 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -2,6 +2,7 @@ #define _SWAY_NODE_H #include #include +#include #include "list.h" #define MIN_SANE_W 100 @@ -75,4 +76,15 @@ list_t *node_get_children(struct sway_node *node); bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); +// when destroying a sway tree, it's not known which order the tree will be +// destroyed. To prevent freeing of scene_nodes recursing up the tree, +// let's use this helper function to disown them to the staging node. +void scene_node_disown_children(struct wlr_scene_tree *tree); + +// a helper function used to allocate tree nodes. If an allocation failure +// occurs a flag is flipped that can be checked later to destroy a parent +// of this scene node preventing memory leaks. +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed); + #endif diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index b3dda12f9..9cb3d7bfa 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -16,6 +17,31 @@ struct sway_root { struct wlr_output_layout *output_layout; struct wl_listener output_layout_change; + + // scene node layout: + // - root + // - staging + // - layer shell stuff + // - tiling + // - floating + // - fullscreen stuff + // - seat stuff + // - ext_session_lock + struct wlr_scene *root_scene; + + // since wlr_scene nodes can't be orphaned and must always + // have a parent, use this staging scene_tree so that a + // node always have a valid parent. Nothing in this + // staging node will be visible. + struct wlr_scene_tree *staging; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *floating; + struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *fullscreen_global; + } layers; + #if HAVE_XWAYLAND struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 822e7bb33..4aaed9e3e 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -2,6 +2,7 @@ #define _SWAY_VIEW_H #include #include +#include #include "sway/config.h" #if HAVE_XWAYLAND #include @@ -69,6 +70,9 @@ struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *content_tree; + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index b0fef4ca7..58bde20cb 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -2,6 +2,7 @@ #define _SWAY_WORKSPACE_H #include +#include #include "sway/config.h" #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -23,6 +24,12 @@ struct sway_workspace_state { struct sway_workspace { struct sway_node node; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + } layers; + struct sway_container *fullscreen; char *name; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 928c77d6d..288ccc7c8 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -856,9 +856,6 @@ void output_damage_whole_container(struct sway_output *output, .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); - if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { - wlr_output_schedule_frame(output->wlr_output); - } // Damage subsurfaces as well, which may extend outside the box if (con->view) { damage_child_views_iterator(con, output); @@ -914,6 +911,8 @@ static void begin_destroy(struct sway_output *output) { wlr_damage_ring_finish(&output->damage_ring); + wlr_scene_output_destroy(output->scene_output); + output->scene_output = NULL; output->wlr_output->data = NULL; output->wlr_output = NULL; @@ -1039,11 +1038,24 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } - struct sway_output *output = output_create(wlr_output); - if (!output) { + // Create the scene output here so we're not accidentally creating one for + // the fallback output + struct wlr_scene_output *scene_output = + wlr_scene_output_create(root->root_scene, wlr_output); + if (!scene_output) { + sway_log(SWAY_ERROR, "Failed to create a scene output"); return; } + + struct sway_output *output = output_create(wlr_output); + if (!output) { + sway_log(SWAY_ERROR, "Failed to create a sway output"); + wlr_scene_output_destroy(scene_output); + return; + } + output->server = server; + output->scene_output = scene_output; wlr_damage_ring_init(&output->damage_ring); wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); diff --git a/sway/tree/container.c b/sway/tree/container.c index 8c344a6d4..9ed089297 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -27,6 +27,24 @@ #include "log.h" #include "stringop.h" +static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, + bool *failed) { + if (*failed) { + return NULL; + } + + // just pass in random values. These will be overwritten when + // they need to be used. + struct wlr_scene_rect *rect = wlr_scene_rect_create( + parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); + if (!rect) { + sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect"); + *failed = true; + } + + return rect; +} + struct sway_container *container_create(struct sway_view *view) { struct sway_container *c = calloc(1, sizeof(struct sway_container)); if (!c) { @@ -34,14 +52,62 @@ struct sway_container *container_create(struct sway_view *view) { return NULL; } node_init(&c->node, N_CONTAINER, c); - c->pending.layout = L_NONE; - c->view = view; - c->alpha = 1.0f; + + // Container tree structure + // - scene tree + // - title bar + // - border + // - background + // - title text + // - marks text + // - border + // - border top/bottom/left/right + // - content_tree (we put the content node here so when we disable the + // border everything gets disabled. We only render the content iff there + // is a border as well) + bool failed = false; + c->scene_tree = alloc_scene_tree(root->staging, &failed); + + c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed); + c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed); + c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed); + + // for opacity purposes we need to carfully create the scene such that + // none of our rect nodes as well as text buffers don't overlap. To do + // this we have to create rects such that they go around text buffers + for (int i = 0; i < 4; i++) { + alloc_rect_node(c->title_bar.border, &failed); + } + + for (int i = 0; i < 5; i++) { + alloc_rect_node(c->title_bar.background, &failed); + } + + c->border.tree = alloc_scene_tree(c->scene_tree, &failed); + c->content_tree = alloc_scene_tree(c->border.tree, &failed); + + if (view) { + // only containers with views can have borders + c->border.top = alloc_rect_node(c->border.tree, &failed); + c->border.bottom = alloc_rect_node(c->border.tree, &failed); + c->border.left = alloc_rect_node(c->border.tree, &failed); + c->border.right = alloc_rect_node(c->border.tree, &failed); + } + + if (failed) { + wlr_scene_node_destroy(&c->scene_tree->node); + free(c); + return NULL; + } if (!view) { c->pending.children = create_list(); c->current.children = create_list(); } + + c->pending.layout = L_NONE; + c->view = view; + c->alpha = 1.0f; c->marks = create_list(); c->outputs = create_list(); @@ -85,6 +151,8 @@ void container_destroy(struct sway_container *con) { } } + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); free(con); } diff --git a/sway/tree/node.c b/sway/tree/node.c index 12361c75b..213cf0a6b 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -159,3 +159,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { } return false; } + +void scene_node_disown_children(struct wlr_scene_tree *tree) { + // this function can be called as part of destruction code that will be invoked + // upon an allocation failure. Let's not crash on NULL due to an allocation error. + if (!tree) { + return; + } + + struct wlr_scene_node *child, *tmp_child; + wl_list_for_each_safe(child, tmp_child, &tree->children, link) { + wlr_scene_node_reparent(child, root->staging); + } +} + +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed) { + // fallthrough + if (*failed) { + return NULL; + } + + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + if (!tree) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + *failed = true; + } + + return tree; +} diff --git a/sway/tree/output.c b/sway/tree/output.c index 2186ad0c6..12a2f969e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -87,9 +87,41 @@ static void restore_workspaces(struct sway_output *output) { output_sort_workspaces(output); } +static void destroy_scene_layers(struct sway_output *output) { + wlr_scene_node_destroy(&output->fullscreen_background->node); + + scene_node_disown_children(output->layers.tiling); + scene_node_disown_children(output->layers.fullscreen); + + wlr_scene_node_destroy(&output->layers.tiling->node); + wlr_scene_node_destroy(&output->layers.fullscreen->node); +} + struct sway_output *output_create(struct wlr_output *wlr_output) { struct sway_output *output = calloc(1, sizeof(struct sway_output)); node_init(&output->node, N_OUTPUT, output); + + bool failed = false; + output->layers.tiling = alloc_scene_tree(root->staging, &failed); + output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); + + if (!failed) { + output->fullscreen_background = wlr_scene_rect_create( + output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); + + if (!output->fullscreen_background) { + sway_log(SWAY_ERROR, "Unable to allocate a background rect"); + failed = true; + } + } + + if (failed) { + destroy_scene_layers(output); + wlr_scene_output_destroy(output->scene_output); + free(output); + return NULL; + } + output->wlr_output = wlr_output; wlr_output->data = output; output->detected_subpixel = wlr_output->subpixel; @@ -238,6 +270,8 @@ void output_destroy(struct sway_output *output) { "which is still referenced by transactions")) { return; } + + destroy_scene_layers(output); list_free(output->workspaces); list_free(output->current.workspaces); wl_event_source_remove(output->repaint_timer); diff --git a/sway/tree/root.c b/sway/tree/root.c index dc51c3beb..75fb63f11 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "sway/desktop/transaction.h" #include "sway/input/seat.h" @@ -30,7 +31,33 @@ struct sway_root *root_create(struct wl_display *wl_display) { sway_log(SWAY_ERROR, "Unable to allocate sway_root"); return NULL; } + + struct wlr_scene *root_scene = wlr_scene_create(); + if (!root_scene) { + sway_log(SWAY_ERROR, "Unable to allocate root scene node"); + free(root); + return NULL; + } + node_init(&root->node, N_ROOT, root); + root->root_scene = root_scene; + + bool failed = false; + root->staging = alloc_scene_tree(&root_scene->tree, &failed); + + root->layers.tiling = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.floating = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); + + if (failed) { + wlr_scene_node_destroy(&root_scene->tree.node); + free(root); + return NULL; + } + + wlr_scene_node_set_enabled(&root->staging->node, false); + root->output_layout = wlr_output_layout_create(wl_display); wl_list_init(&root->all_outputs); #if HAVE_XWAYLAND @@ -53,6 +80,7 @@ void root_destroy(struct sway_root *root) { list_free(root->scratchpad); list_free(root->non_desktop_outputs); list_free(root->outputs); + wlr_scene_node_destroy(&root->root_scene->tree.node); free(root); } diff --git a/sway/tree/view.c b/sway/tree/view.c index d62a0667b..bc968edc9 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -37,6 +37,14 @@ bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { + bool failed = false; + view->scene_tree = alloc_scene_tree(root->staging, &failed); + view->content_tree = alloc_scene_tree(view->scene_tree, &failed); + if (failed) { + wlr_scene_node_destroy(&view->scene_tree->node); + return false; + } + view->type = type; view->impl = impl; view->executed_criteria = create_list(); @@ -67,7 +75,7 @@ void view_destroy(struct sway_view *view) { list_free(view->executed_criteria); view_assign_ctx(view, NULL); - + wlr_scene_node_destroy(&view->scene_tree->node); free(view->title_format); if (view->impl->destroy) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 18218768b..f60b23662 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -71,6 +71,18 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); + + bool failed = false; + ws->layers.tiling = alloc_scene_tree(root->staging, &failed); + ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed); + + if (failed) { + wlr_scene_node_destroy(&ws->layers.tiling->node); + wlr_scene_node_destroy(&ws->layers.fullscreen->node); + free(ws); + return NULL; + } + ws->name = strdup(name); ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); @@ -131,6 +143,11 @@ void workspace_destroy(struct sway_workspace *workspace) { return; } + scene_node_disown_children(workspace->layers.tiling); + scene_node_disown_children(workspace->layers.fullscreen); + wlr_scene_node_destroy(&workspace->layers.tiling->node); + wlr_scene_node_destroy(&workspace->layers.fullscreen->node); + free(workspace->name); free(workspace->representation); list_free_items_and_destroy(workspace->output_priority); From 1b092386455eeb16fed05cea672c4c88d733f8f2 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 13 Apr 2023 20:45:02 +0200 Subject: [PATCH 07/36] scene_graph: Use built-in linux dmabuf feedback handling --- sway/main.c | 4 ++++ sway/tree/container.c | 27 --------------------------- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/sway/main.c b/sway/main.c index 98aba7f20..65c85d31f 100644 --- a/sway/main.c +++ b/sway/main.c @@ -340,6 +340,10 @@ int main(int argc, char **argv) { return 1; } + if (server.linux_dmabuf_v1) { + wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); + } + if (validate) { bool valid = load_main_config(config_path, false, true); free(config_path); diff --git a/sway/tree/container.c b/sway/tree/container.c index 9ed089297..307bf9633 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1157,33 +1157,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) { con->view->foreign_toplevel, enable); } } - - if (!server.linux_dmabuf_v1 || !con->view->surface) { - return; - } - if (!enable) { - wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, - con->view->surface, NULL); - return; - } - - if (!con->pending.workspace || !con->pending.workspace->output) { - return; - } - - struct sway_output *output = con->pending.workspace->output; - - const struct wlr_linux_dmabuf_feedback_v1_init_options options = { - .main_renderer = server.renderer, - .scanout_primary_output = output->wlr_output, - }; - struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; - if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &options)) { - return; - } - wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, - con->view->surface, &feedback); - wlr_linux_dmabuf_feedback_v1_finish(&feedback); } static void container_fullscreen_workspace(struct sway_container *con) { From 0e1a02bf0aad3d743985602b55043c5de019d1f0 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 21 Nov 2023 19:05:37 -0500 Subject: [PATCH 08/36] scene_graph: Introduce sway_scene_descriptor Across a wayland compositor, there are multiple shells: It can be a toplevel, or a layer_shell, or even something more meta like a drag icon or highlight indicators when dragging windows around. This object lets us store values that represent these modes of operation and keep track of what object is being represented. --- include/sway/scene_descriptor.h | 25 +++++++++++++ sway/meson.build | 1 + sway/scene_descriptor.c | 66 +++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 include/sway/scene_descriptor.h create mode 100644 sway/scene_descriptor.c diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h new file mode 100644 index 000000000..13ae81a37 --- /dev/null +++ b/include/sway/scene_descriptor.h @@ -0,0 +1,25 @@ +/** + * Across a wayland compositor, there are multiple shells: It can be + * a toplevel, or a layer_shell, or even something more meta like a drag + * icon or highlight indicators when dragging windows around. + * + * This object lets us store values that represent these modes of operation + * and keep track of what object is being represented. + */ +#ifndef _SWAY_SCENE_DESCRIPTOR_H +#define _SWAY_SCENE_DESCRIPTOR_H +#include + +enum sway_scene_descriptor_type { +}; + +bool scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data); + +void *scene_descriptor_try_get(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type); + +void scene_descriptor_destroy(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type); + +#endif diff --git a/sway/meson.build b/sway/meson.build index 3abd778db..26251e586 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -8,6 +8,7 @@ sway_sources = files( 'lock.c', 'main.c', 'realtime.c', + 'scene_descriptor.c', 'server.c', 'swaynag.c', 'xdg_activation_v1.c', diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c new file mode 100644 index 000000000..a30d46646 --- /dev/null +++ b/sway/scene_descriptor.c @@ -0,0 +1,66 @@ +#include +#include +#include "log.h" +#include "sway/scene_descriptor.h" + +struct scene_descriptor { + void *data; + struct wlr_addon addon; +}; + +static const struct wlr_addon_interface addon_interface; + +static struct scene_descriptor *scene_node_get_descriptor( + struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { + struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface); + if (!addon) { + return NULL; + } + + struct scene_descriptor *desc = wl_container_of(addon, desc, addon); + return desc; +} + +static void descriptor_destroy(struct scene_descriptor *desc) { + wlr_addon_finish(&desc->addon); + free(desc); +} + +void *scene_descriptor_try_get(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type) { + struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + if (!desc) { + return NULL; + } + + return desc->data; +} + +void scene_descriptor_destroy(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type) { + struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + descriptor_destroy(desc); +} + +static void addon_handle_destroy(struct wlr_addon *addon) { + struct scene_descriptor *desc = wl_container_of(addon, desc, addon); + descriptor_destroy(desc); +} + +static const struct wlr_addon_interface addon_interface = { + .name = "sway_scene_descriptor", + .destroy = addon_handle_destroy, +}; + +bool scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data) { + struct scene_descriptor *desc = calloc(1, sizeof(*desc)); + if (!desc) { + sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); + return false; + } + + wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface); + desc->data = data; + return true; +} From 9c17cba0b29979ae23c4521b884f7419fd558770 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 4 Dec 2023 06:33:57 -0500 Subject: [PATCH 09/36] renderer: Render scene_graph --- include/sway/output.h | 3 - include/sway/scene_descriptor.h | 1 + include/sway/server.h | 2 - include/sway/surface.h | 14 - sway/desktop/output.c | 462 +++++++------------------------- sway/desktop/surface.c | 43 +-- sway/server.c | 3 - 7 files changed, 101 insertions(+), 427 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 691ac8ddd..b35f13667 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -41,7 +41,6 @@ struct sway_output { struct wl_list shell_layers[4]; // sway_layer_surface::link struct wlr_box usable_area; - struct timespec last_frame; struct wlr_damage_ring damage_ring; int lx, ly; // layout coords @@ -58,9 +57,7 @@ struct sway_output { struct wl_listener destroy; struct wl_listener commit; struct wl_listener present; - struct wl_listener damage; struct wl_listener frame; - struct wl_listener needs_frame; struct wl_listener request_state; struct { diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 13ae81a37..9761c2c02 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -11,6 +11,7 @@ #include enum sway_scene_descriptor_type { + SWAY_SCENE_DESC_BUFFER_TIMER, }; bool scene_descriptor_assign(struct wlr_scene_node *node, diff --git a/include/sway/server.h b/include/sway/server.h index b0e8dfd66..f3d259808 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -41,7 +41,6 @@ struct sway_server { struct wlr_allocator *allocator; struct wlr_compositor *compositor; - struct wl_listener compositor_new_surface; struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; @@ -170,7 +169,6 @@ void server_run(struct sway_server *server); void restore_nofile_limit(void); -void handle_compositor_new_surface(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); diff --git a/include/sway/surface.h b/include/sway/surface.h index a7a8ec3ff..81eb80d59 100644 --- a/include/sway/surface.h +++ b/include/sway/surface.h @@ -2,20 +2,6 @@ #define _SWAY_SURFACE_H #include -struct sway_surface { - struct wlr_surface *wlr_surface; - - struct wl_listener destroy; - - /** - * This timer can be used for issuing delayed frame done callbacks (for - * example, to improve presentation latency). Its handler is set to a - * function that issues a frame done callback to this surface. - */ - struct wl_event_source *frame_done_timer; -}; - -void surface_update_outputs(struct wlr_surface *surface); void surface_enter_output(struct wlr_surface *surface, struct sway_output *output); void surface_leave_output(struct wlr_surface *surface, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 288ccc7c8..11de25fb6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -26,8 +26,8 @@ #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -275,104 +275,6 @@ void output_drag_icons_for_each_surface(struct sway_output *output, } } -static void for_each_surface_container_iterator(struct sway_container *con, - void *_data) { - if (!con->view || !view_is_visible(con->view)) { - return; - } - - struct surface_iterator_data *data = _data; - output_view_for_each_surface(data->output, con->view, - data->user_iterator, data->user_data); -} - -static void output_for_each_surface(struct sway_output *output, - sway_surface_iterator_func_t iterator, void *user_data) { - if (server.session_lock.locked) { - if (server.session_lock.lock == NULL) { - return; - } - struct wlr_session_lock_surface_v1 *lock_surface; - wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { - if (lock_surface->output != output->wlr_output) { - continue; - } - if (!lock_surface->surface->mapped) { - continue; - } - - output_surface_for_each_surface(output, lock_surface->surface, - 0.0, 0.0, iterator, user_data); - } - return; - } - - if (output_has_opaque_overlay_layer_surface(output)) { - goto overlay; - } - - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - }; - - struct sway_workspace *workspace = output_get_active_workspace(output); - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - if (!workspace) { - return; - } - fullscreen_con = workspace->current.fullscreen; - } - if (fullscreen_con) { - for_each_surface_container_iterator(fullscreen_con, &data); - container_for_each_child(fullscreen_con, - for_each_surface_container_iterator, &data); - - // TODO: Show transient containers for fullscreen global - if (fullscreen_con == workspace->current.fullscreen) { - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, fullscreen_con)) { - for_each_surface_container_iterator(floater, &data); - } - } - } -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, - iterator, user_data); -#endif - } else { - output_layer_for_each_surface(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - iterator, user_data); - output_layer_for_each_surface(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - iterator, user_data); - - workspace_for_each_container(workspace, - for_each_surface_container_iterator, &data); - -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, - iterator, user_data); -#endif - output_layer_for_each_surface(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - iterator, user_data); - } - -overlay: - output_layer_for_each_surface(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - iterator, user_data); - output_drag_icons_for_each_surface(output, &root->drag_icons, - iterator, user_data); -} - static int scale_length(int length, int offset, float scale) { return roundf((offset + length) * scale) - roundf(offset * scale); } @@ -424,276 +326,137 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { struct send_frame_done_data { struct timespec when; int msec_until_refresh; + struct sway_output *output; }; -static void send_frame_done_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) { - int view_max_render_time = 0; - if (view != NULL) { - view_max_render_time = view->max_render_time; +struct buffer_timer { + struct wl_listener destroy; + struct wl_event_source *frame_done_timer; +}; + +static int handle_buffer_timer(void *data) { + struct wlr_scene_buffer *buffer = data; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_buffer_send_frame_done(buffer, &now); + return 0; +} + +static void handle_buffer_timer_destroy(struct wl_listener *listener, + void *data) { + struct buffer_timer *timer = wl_container_of(listener, timer, destroy); + + wl_list_remove(&timer->destroy.link); + wl_event_source_remove(timer->frame_done_timer); + free(timer); +} + +static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { + struct buffer_timer *timer = + scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); + if (timer) { + return timer; } + timer = calloc(1, sizeof(struct buffer_timer)); + if (!timer) { + return NULL; + } + + timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_buffer_timer, buffer); + if (!timer->frame_done_timer) { + free(timer); + return NULL; + } + + scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); + + timer->destroy.notify = handle_buffer_timer_destroy; + wl_signal_add(&buffer->node.events.destroy, &timer->destroy); + + return timer; +} + +static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, + int x, int y, void *user_data) { struct send_frame_done_data *data = user_data; + struct sway_output *output = data->output; + int view_max_render_time = 0; + + if (buffer->primary_output != data->output->scene_output) { + return; + } + + struct wlr_scene_node *current = &buffer->node; + + while (true) { + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } int delay = data->msec_until_refresh - output->max_render_time - view_max_render_time; - if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { - wlr_surface_send_frame_done(surface, &data->when); + struct buffer_timer *timer = NULL; + + if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { + timer = buffer_timer_get_or_create(buffer); + } + + if (timer) { + wl_event_source_timer_update(timer->frame_done_timer, delay); } else { - struct sway_surface *sway_surface = surface->data; - wl_event_source_timer_update(sway_surface->frame_done_timer, delay); - } -} - -static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { - output_for_each_surface(output, send_frame_done_iterator, data); -} - -static void count_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *data) { - size_t *n = data; - (*n)++; -} - -static bool scan_out_fullscreen_view(struct sway_output *output, - struct wlr_output_state *pending, struct sway_view *view) { - struct wlr_output *wlr_output = output->wlr_output; - struct sway_workspace *workspace = output->current.active_workspace; - if (!sway_assert(workspace, "Expected an active workspace")) { - return false; - } - - if (server.session_lock.locked) { - return false; - } - - if (!wl_list_empty(&view->saved_buffers)) { - return false; - } - - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, view->container)) { - return false; - } - } - -#if HAVE_XWAYLAND - if (!wl_list_empty(&root->xwayland_unmanaged)) { - return false; - } -#endif - - if (!wl_list_empty(&output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { - return false; - } - if (!wl_list_empty(&root->drag_icons)) { - return false; - } - - struct wlr_surface *surface = view->surface; - if (surface == NULL) { - return false; - } - size_t n_surfaces = 0; - output_view_for_each_surface(output, view, - count_surface_iterator, &n_surfaces); - if (n_surfaces != 1) { - return false; - } - size_t n_popups = 0; - output_view_for_each_popup_surface(output, view, - count_surface_iterator, &n_popups); - if (n_popups > 0) { - return false; - } - - if (surface->buffer == NULL) { - return false; - } - - if ((float)surface->current.scale != wlr_output->scale || - surface->current.transform != wlr_output->transform) { - return false; - } - - if (!wlr_output_is_direct_scanout_allowed(wlr_output)) { - return false; - } - - wlr_output_state_set_buffer(pending, &surface->buffer->base); - - if (!wlr_output_test_state(wlr_output, pending)) { - return false; - } - - wlr_presentation_surface_scanned_out_on_output(surface, - wlr_output); - - return wlr_output_commit_state(wlr_output, pending); -} - -static void get_frame_damage(struct sway_output *output, - pixman_region32_t *frame_damage) { - struct wlr_output *wlr_output = output->wlr_output; - - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - - pixman_region32_init(frame_damage); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(frame_damage, &output->damage_ring.current, - transform, width, height); - - if (debug.damage != DAMAGE_DEFAULT) { - pixman_region32_union_rect(frame_damage, frame_damage, - 0, 0, wlr_output->width, wlr_output->height); + wlr_scene_buffer_send_frame_done(buffer, &data->when); } } static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; - struct wlr_output *wlr_output = output->wlr_output; - if (wlr_output == NULL) { + + if (!output->enabled) { return 0; } - wlr_output->frame_pending = false; - - if (!wlr_output->needs_frame && - !output->gamma_lut_changed && - !pixman_region32_not_empty(&output->damage_ring.current)) { - return 0; - } - - struct sway_workspace *workspace = output->current.active_workspace; - if (workspace == NULL) { - return 0; - } - - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - fullscreen_con = workspace->current.fullscreen; - } - - struct wlr_output_state pending = {0}; + output->wlr_output->frame_pending = false; if (output->gamma_lut_changed) { + struct wlr_output_state pending; + wlr_output_state_init(&pending); + if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { + return 0; + } + output->gamma_lut_changed = false; struct wlr_gamma_control_v1 *gamma_control = wlr_gamma_control_manager_v1_get_control( - server.gamma_control_manager_v1, wlr_output); + server.gamma_control_manager_v1, output->wlr_output); if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { - goto out; - } - if (!wlr_output_test_state(wlr_output, &pending)) { wlr_output_state_finish(&pending); - pending = (struct wlr_output_state){0}; + return 0; + } + + if (!wlr_output_commit_state(output->wlr_output, &pending)) { wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + wlr_output_state_finish(&pending); + return 0; } + + wlr_output_state_finish(&pending); + return 0; } - pending.committed |= WLR_OUTPUT_STATE_DAMAGE; - get_frame_damage(output, &pending.damage); - - if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { - // Try to scan-out the fullscreen view - static bool last_scanned_out = false; - bool scanned_out = - scan_out_fullscreen_view(output, &pending, fullscreen_con->view); - - if (scanned_out && !last_scanned_out) { - sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", - output->wlr_output->name); - } - if (last_scanned_out && !scanned_out) { - sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", - output->wlr_output->name); - output_damage_whole(output); - } - last_scanned_out = scanned_out; - - if (scanned_out) { - goto out; - } - } - - if (!wlr_output_configure_primary_swapchain(wlr_output, &pending, &wlr_output->swapchain)) { - goto out; - } - - int buffer_age; - struct wlr_buffer *buffer = wlr_swapchain_acquire(wlr_output->swapchain, &buffer_age); - if (buffer == NULL) { - goto out; - } - - struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass( - wlr_output->renderer, buffer, NULL); - if (render_pass == NULL) { - wlr_buffer_unlock(buffer); - goto out; - } - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage); - - if (debug.damage == DAMAGE_RERENDER) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(&damage, &damage, 0, 0, width, height); - } - - struct render_context ctx = { - .output_damage = &damage, - .renderer = wlr_output->renderer, - .output = output, - .pass = render_pass, - }; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - output_render(&ctx); - - pixman_region32_fini(&damage); - - if (!wlr_render_pass_submit(render_pass)) { - wlr_buffer_unlock(buffer); - goto out; - } - - wlr_output_state_set_buffer(&pending, buffer); - wlr_buffer_unlock(buffer); - - if (!wlr_output_commit_state(wlr_output, &pending)) { - goto out; - } + wlr_scene_output_commit(output->scene_output, NULL); wlr_damage_ring_rotate(&output->damage_ring); - output->last_frame = now; -out: - wlr_output_state_finish(&pending); return 0; } -static void handle_damage(struct wl_listener *listener, void *user_data) { - struct sway_output *output = - wl_container_of(listener, output, damage); - struct wlr_output_event_damage *event = user_data; - if (wlr_damage_ring_add(&output->damage_ring, event->damage)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - static void handle_frame(struct wl_listener *listener, void *user_data) { struct sway_output *output = wl_container_of(listener, output, frame); @@ -754,13 +517,8 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { struct send_frame_done_data data = {0}; clock_gettime(CLOCK_MONOTONIC, &data.when); data.msec_until_refresh = msec_until_refresh; - send_frame_done(output, &data); -} - -static void handle_needs_frame(struct wl_listener *listener, void *user_data) { - struct sway_output *output = - wl_container_of(listener, output, needs_frame); - wlr_output_schedule_frame(output->wlr_output); + data.output = output; + wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); } void output_damage_whole(struct sway_output *output) { @@ -904,9 +662,7 @@ static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->present.link); - wl_list_remove(&output->damage.link); wl_list_remove(&output->frame.link); - wl_list_remove(&output->needs_frame.link); wl_list_remove(&output->request_state.link); wlr_damage_ring_finish(&output->damage_ring); @@ -931,17 +687,6 @@ static void handle_layout_destroy(struct wl_listener *listener, void *data) { begin_destroy(output); } -static void update_textures(struct sway_container *con, void *data) { - container_update_title_textures(con); - container_update_marks_textures(con); -} - -static void update_output_scale_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) { - surface_update_outputs(surface); -} - static void handle_commit(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, commit); struct wlr_output_event_commit *event = data; @@ -950,11 +695,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->state->committed & WLR_OUTPUT_STATE_SCALE) { - output_for_each_container(output, update_textures, NULL); - output_for_each_surface(output, update_output_scale_iterator, NULL); - } - if (event->state->committed & ( WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | @@ -1066,12 +806,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->commit.notify = handle_commit; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&wlr_output->events.damage, &output->damage); - output->damage.notify = handle_damage; wl_signal_add(&wlr_output->events.frame, &output->frame); output->frame.notify = handle_frame; - wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame); - output->needs_frame.notify = handle_needs_frame; wl_signal_add(&wlr_output->events.request_state, &output->request_state); output->request_state.notify = handle_request_state; diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c index 5932eaa23..af17a8bb6 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c @@ -1,53 +1,12 @@ #define _POSIX_C_SOURCE 200112L #include -#include #include #include #include "sway/server.h" #include "sway/surface.h" #include "sway/output.h" -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_surface *surface = wl_container_of(listener, surface, destroy); - - surface->wlr_surface->data = NULL; - wl_list_remove(&surface->destroy.link); - - if (surface->frame_done_timer) { - wl_event_source_remove(surface->frame_done_timer); - } - - free(surface); -} - -static int surface_frame_done_timer_handler(void *data) { - struct sway_surface *surface = data; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(surface->wlr_surface, &now); - - return 0; -} - -void handle_compositor_new_surface(struct wl_listener *listener, void *data) { - struct wlr_surface *wlr_surface = data; - - struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); - surface->wlr_surface = wlr_surface; - wlr_surface->data = surface; - - surface->destroy.notify = handle_destroy; - wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); - - surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, - surface_frame_done_timer_handler, surface); - if (!surface->frame_done_timer) { - wl_resource_post_no_memory(wlr_surface->resource); - } -} - -void surface_update_outputs(struct wlr_surface *surface) { +static void surface_update_outputs(struct wlr_surface *surface) { float scale = 1; struct wlr_surface_output *surface_output; wl_list_for_each(surface_output, &surface->current_outputs, link) { diff --git a/sway/server.c b/sway/server.c index 4bef45884..33b25000b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -201,9 +201,6 @@ bool server_init(struct sway_server *server) { server->compositor = wlr_compositor_create(server->wl_display, 6, server->renderer); - server->compositor_new_surface.notify = handle_compositor_new_surface; - wl_signal_add(&server->compositor->events.new_surface, - &server->compositor_new_surface); wlr_subcompositor_create(server->wl_display); From c640c3015f3a7ea2987bd7854d13ff282f90804f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 21 Nov 2023 19:55:47 -0500 Subject: [PATCH 10/36] scene_graph: Port seat drag icons --- include/sway/input/seat.h | 26 ++++----- include/sway/output.h | 4 -- include/sway/scene_descriptor.h | 1 + include/sway/tree/root.h | 2 +- sway/desktop/output.c | 16 ------ sway/desktop/render.c | 10 ---- sway/input/cursor.c | 10 +--- sway/input/seat.c | 98 +++++++++++---------------------- sway/input/seatop_default.c | 15 +---- sway/tree/root.c | 2 +- 10 files changed, 53 insertions(+), 131 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 28a3ea377..3c81a7136 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -75,20 +76,6 @@ struct sway_seat_node { struct wl_listener destroy; }; -struct sway_drag_icon { - struct sway_seat *seat; - struct wlr_drag_icon *wlr_drag_icon; - struct wl_list link; // sway_root::drag_icons - - double x, y; // in layout-local coordinates - int dx, dy; // offset in surface-local coordinates - - struct wl_listener surface_commit; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; -}; - struct sway_drag { struct sway_seat *seat; struct wlr_drag *wlr_drag; @@ -99,6 +86,15 @@ struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; + // Seat scene tree structure + // - scene_tree + // - drag icons + // - drag icon 1 + // - drag icon 2 + // - seatop specific stuff + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *drag_icons; + bool has_focus; struct wl_list focus_stack; // list of containers in focus order struct sway_workspace *workspace; @@ -257,7 +253,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); -void drag_icon_update_position(struct sway_drag_icon *icon); +void drag_icons_update_position(struct sway_seat *seat); enum wlr_edges find_resize_edge(struct sway_container *cont, struct wlr_surface *surface, struct sway_cursor *cursor); diff --git a/include/sway/output.h b/include/sway/output.h index b35f13667..28240819f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -167,10 +167,6 @@ void output_unmanaged_for_each_surface(struct sway_output *output, void *user_data); #endif -void output_drag_icons_for_each_surface(struct sway_output *output, - struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, - void *user_data); - void output_for_each_workspace(struct sway_output *output, void (*f)(struct sway_workspace *ws, void *data), void *data); diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 9761c2c02..057993ec6 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -12,6 +12,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_DRAG_ICON, }; bool scene_descriptor_assign(struct wlr_scene_node *node, diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 9cb3d7bfa..4b48a6514 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -40,12 +40,12 @@ struct sway_root { struct wlr_scene_tree *floating; struct wlr_scene_tree *fullscreen; struct wlr_scene_tree *fullscreen_global; + struct wlr_scene_tree *seat; } layers; #if HAVE_XWAYLAND struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link #endif - struct wl_list drag_icons; // sway_drag_icon::link // Includes disabled outputs struct wl_list all_outputs; // sway_output::link diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 11de25fb6..321e2a72d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -259,22 +259,6 @@ void output_unmanaged_for_each_surface(struct sway_output *output, } #endif -void output_drag_icons_for_each_surface(struct sway_output *output, - struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->lx; - double oy = drag_icon->y - output->ly; - - if (drag_icon->wlr_drag_icon->surface->mapped) { - output_surface_for_each_surface(output, - drag_icon->wlr_drag_icon->surface, ox, oy, - iterator, user_data); - } - } -} - static int scale_length(int length, int offset, float scale) { return roundf((offset + length) * scale) - roundf(offset * scale); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 168c941bd..735dddb88 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -184,15 +184,6 @@ static void render_unmanaged(struct render_context *ctx, struct wl_list *unmanag } #endif -static void render_drag_icons(struct render_context *ctx, struct wl_list *drag_icons) { - struct render_data data = { - .alpha = 1.0f, - .ctx = ctx, - }; - output_drag_icons_for_each_surface(ctx->output, drag_icons, - render_surface_iterator, &data); -} - // _box.x and .y are expected to be layout-local // _box.width and .height are expected to be output-buffer-local void render_rect(struct render_context *ctx, const struct wlr_box *_box, @@ -1131,7 +1122,6 @@ render_overlay: &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_layer_popups(ctx, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(ctx, &root->drag_icons); renderer_end: pixman_region32_fini(&transformed_damage); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 73aef4b03..e8604193d 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -19,12 +19,12 @@ #include "log.h" #include "util.h" #include "sway/commands.h" -#include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" @@ -543,12 +543,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { if (seat->touch_id == event->touch_id) { seat->touch_x = lx; seat->touch_y = ly; - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + + drag_icons_update_position(seat); } if (cursor->simulating_pointer_from_touch) { diff --git a/sway/input/seat.c b/sway/input/seat.c index cb8ac6a4f..d2cd1ba4a 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -18,7 +18,7 @@ #include "list.h" #include "log.h" #include "sway/config.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" @@ -92,6 +92,7 @@ void seat_destroy(struct sway_seat *seat) { for (int i = 0; i < seat->deferred_bindings->length; i++) { free_sway_binding(seat->deferred_bindings->items[i]); } + wlr_scene_node_destroy(&seat->scene_tree->node); list_free(seat->deferred_bindings); free(seat->prev_workspace_name); free(seat); @@ -353,25 +354,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { seat_node_from_node(seat, node); } -static void drag_icon_damage_whole(struct sway_drag_icon *icon) { - if (!icon->wlr_drag_icon->surface->mapped) { - return; - } - desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); -} - -void drag_icon_update_position(struct sway_drag_icon *icon) { - drag_icon_damage_whole(icon); - - struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; - struct sway_seat *seat = icon->seat; +static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) { + struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); struct wlr_cursor *cursor = seat->cursor->cursor; + switch (wlr_icon->drag->grab_type) { case WLR_DRAG_GRAB_KEYBOARD: return; case WLR_DRAG_GRAB_KEYBOARD_POINTER: - icon->x = cursor->x + icon->dx; - icon->y = cursor->y + icon->dy; + wlr_scene_node_set_position(node, cursor->x, cursor->y); break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -379,42 +370,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x + icon->dx; - icon->y = seat->touch_y + icon->dy; + wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); } - - drag_icon_damage_whole(icon); } -static void drag_icon_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_drag_icon *icon = - wl_container_of(listener, icon, surface_commit); - struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; - icon->dx += wlr_icon->surface->current.dx; - icon->dy += wlr_icon->surface->current.dy; - drag_icon_update_position(icon); -} - -static void drag_icon_handle_map(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, map); - drag_icon_damage_whole(icon); -} - -static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); - drag_icon_damage_whole(icon); -} - -static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); - icon->wlr_drag_icon->data = NULL; - wl_list_remove(&icon->link); - wl_list_remove(&icon->surface_commit.link); - wl_list_remove(&icon->unmap.link); - wl_list_remove(&icon->map.link); - wl_list_remove(&icon->destroy.link); - free(icon); +void drag_icons_update_position(struct sway_seat *seat) { + struct wlr_scene_node *node; + wl_list_for_each(node, &seat->drag_icons->children, link) { + drag_icon_update_position(seat, node); + } } static void drag_handle_destroy(struct wl_listener *listener, void *data) { @@ -486,27 +450,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; if (wlr_drag_icon != NULL) { - struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); - if (icon == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); + struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon); + if (!tree) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); return; } - icon->seat = seat; - icon->wlr_drag_icon = wlr_drag_icon; - wlr_drag_icon->data = icon; - icon->surface_commit.notify = drag_icon_handle_surface_commit; - wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); - icon->unmap.notify = drag_icon_handle_unmap; - wl_signal_add(&wlr_drag_icon->surface->events.unmap, &icon->unmap); - icon->map.notify = drag_icon_handle_map; - wl_signal_add(&wlr_drag_icon->surface->events.map, &icon->map); - icon->destroy.notify = drag_icon_handle_destroy; - wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); + if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, + wlr_drag_icon)) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); + wlr_scene_node_destroy(&tree->node); + return; + } - wl_list_insert(&root->drag_icons, &icon->link); - - drag_icon_update_position(icon); + drag_icon_update_position(seat, &tree->node); } seatop_begin_default(seat); } @@ -553,8 +510,18 @@ struct sway_seat *seat_create(const char *seat_name) { return NULL; } + bool failed = false; + seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed); + seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed); + if (failed) { + wlr_scene_node_destroy(&seat->scene_tree->node); + free(seat); + return NULL; + } + seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { + wlr_scene_node_destroy(&seat->scene_tree->node); free(seat); return NULL; } @@ -562,6 +529,7 @@ struct sway_seat *seat_create(const char *seat_name) { seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { + wlr_scene_node_destroy(&seat->scene_tree->node); wlr_seat_destroy(seat->wlr_seat); free(seat); return NULL; diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 1dce6dae2..62261c776 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -12,6 +12,7 @@ #include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" @@ -620,12 +621,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); } - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + drag_icons_update_position(seat); e->previous_node = node; } @@ -655,12 +651,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); } - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + drag_icons_update_position(seat); e->previous_node = node; } diff --git a/sway/tree/root.c b/sway/tree/root.c index 75fb63f11..38fcdb7c8 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -49,6 +49,7 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->layers.floating = alloc_scene_tree(&root_scene->tree, &failed); root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.seat = alloc_scene_tree(&root_scene->tree, &failed); if (failed) { wlr_scene_node_destroy(&root_scene->tree.node); @@ -63,7 +64,6 @@ struct sway_root *root_create(struct wl_display *wl_display) { #if HAVE_XWAYLAND wl_list_init(&root->xwayland_unmanaged); #endif - wl_list_init(&root->drag_icons); wl_signal_init(&root->events.new_node); root->outputs = create_list(); root->non_desktop_outputs = create_list(); From 0639bde9fb5637fbe63c65bfe85660745f159b96 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 21 Nov 2023 19:19:01 -0500 Subject: [PATCH 11/36] scene_graph: Port seatop_move_tiling indicators --- sway/input/seatop_move_tiling.c | 71 +++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 26704d0d9..7de39ff60 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -2,7 +2,6 @@ #include #include #include -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -24,28 +23,17 @@ struct seatop_move_tiling_event { struct sway_container *con; struct sway_node *target_node; enum wlr_edges target_edge; - struct wlr_box drop_box; double ref_lx, ref_ly; // cursor's x/y at start of op bool threshold_reached; bool split_target; bool insert_after_target; + struct wlr_scene_rect *indicator_rect; }; -static void handle_render(struct sway_seat *seat, struct render_context *ctx) { +static void handle_end(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; - if (!e->threshold_reached) { - return; - } - if (e->target_node && node_get_output(e->target_node) == ctx->output) { - float color[4]; - memcpy(&color, config->border_colors.focused.indicator, - sizeof(float) * 4); - premultiply_alpha(color, 0.5); - struct wlr_box box; - memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); - scale_box(&box, ctx->output->wlr_output->scale); - render_rect(ctx, &box, color); - } + wlr_scene_node_destroy(&e->indicator_rect->node); + e->indicator_rect = NULL; } static void handle_motion_prethreshold(struct sway_seat *seat) { @@ -66,6 +54,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { // If the threshold has been exceeded, start the actual drag if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, true); e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } @@ -164,6 +153,11 @@ static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, return false; } +static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) { + wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y); + wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height); +} + static void handle_motion_postthreshold(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; e->split_target = false; @@ -172,8 +166,6 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { struct sway_cursor *cursor = seat->cursor; struct sway_node *node = node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - // Damage the old location - desktop_damage_box(&e->drop_box); if (!node) { // Eg. hovered over a layer surface such as swaybar @@ -186,8 +178,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Empty workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; - workspace_get_box(node->sway_workspace, &e->drop_box); - desktop_damage_box(&e->drop_box); + + struct wlr_box drop_box; + workspace_get_box(node->sway_workspace, &drop_box); + update_indicator(e, &drop_box); return; } @@ -200,11 +194,18 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { return; } + struct wlr_box drop_box = { + .x = con->pending.content_x, + .y = con->pending.content_y, + .width = con->pending.content_width, + .height = con->pending.content_height, + }; + // Check if the cursor is over a tilebar only if the destination // container is not a descendant of the source container. if (!surface && !container_has_ancestor(con, e->con) && split_titlebar(node, e->con, cursor->cursor, - &e->drop_box, &e->insert_after_target)) { + &drop_box, &e->insert_after_target)) { // Don't allow dropping over the source container's titlebar // to give users a chance to cancel a drag operation. if (con == e->con) { @@ -214,6 +215,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->split_target = true; } e->target_edge = WLR_EDGE_NONE; + update_indicator(e, &drop_box); return; } @@ -255,8 +257,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->target_node = node_get_parent(e->target_node); } e->target_edge = edge; - e->drop_box = box; - desktop_damage_box(&e->drop_box); + update_indicator(e, &box); return; } con = con->pending.parent; @@ -298,12 +299,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } e->target_node = node; - e->drop_box.x = con->pending.content_x; - e->drop_box.y = con->pending.content_y; - e->drop_box.width = con->pending.content_width; - e->drop_box.height = con->pending.content_height; - resize_box(&e->drop_box, e->target_edge, thickness); - desktop_damage_box(&e->drop_box); + resize_box(&drop_box, e->target_edge, thickness); + update_indicator(e, &drop_box); } static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { @@ -438,7 +435,7 @@ static const struct sway_seatop_impl seatop_impl = { .pointer_motion = handle_pointer_motion, .tablet_tool_tip = handle_tablet_tool_tip, .unref = handle_unref, - .render = handle_render, + .end = handle_end, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, @@ -450,6 +447,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, if (!e) { return; } + + const float *indicator = config->border_colors.focused.indicator; + float color[4] = { + indicator[0] * .5, + indicator[1] * .5, + indicator[2] * .5, + indicator[3] * .5, + }; + e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color); + if (!e->indicator_rect) { + free(e); + return; + } + e->con = con; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; From 9a579666068d62b9354a39941e1ac8c1f4a58093 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 18 Jan 2024 10:04:26 -0500 Subject: [PATCH 12/36] scene_graph: Port ext_session_v1 --- include/sway/output.h | 1 + include/sway/server.h | 25 ++- include/sway/tree/root.h | 1 + sway/desktop/output.c | 4 + sway/desktop/render.c | 39 ----- sway/input/cursor.c | 18 -- sway/input/keyboard.c | 2 +- sway/input/seat.c | 19 +-- sway/input/switch.c | 2 +- sway/lock.c | 358 +++++++++++++++++++++++++++------------ sway/tree/output.c | 2 + sway/tree/root.c | 1 + 12 files changed, 280 insertions(+), 192 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 28240819f..8405f78dc 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -24,6 +24,7 @@ struct sway_output { struct { struct wlr_scene_tree *tiling; struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *session_lock; } layers; // when a container is fullscreen, in case the fullscreen surface is diff --git a/include/sway/server.h b/include/sway/server.h index f3d259808..33ffbf094 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -28,6 +28,19 @@ struct sway_transaction; +struct sway_session_lock { + struct wlr_session_lock_v1 *lock; + struct wlr_surface *focused; + bool abandoned; + + struct wl_list outputs; // struct sway_session_lock_output + + // invalid if the session is abandoned + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; +}; + struct sway_server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; @@ -92,15 +105,9 @@ struct sway_server { struct wl_listener gamma_control_set_gamma; struct { - bool locked; + struct sway_session_lock *lock; struct wlr_session_lock_manager_v1 *manager; - struct wlr_session_lock_v1 *lock; - struct wlr_surface *focused; - struct wl_listener lock_new_surface; - struct wl_listener lock_unlock; - struct wl_listener lock_destroy; - struct wl_listener new_lock; struct wl_listener manager_destroy; } session_lock; @@ -174,6 +181,10 @@ void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); void sway_session_lock_init(void); +void sway_session_lock_add_output(struct sway_session_lock *lock, + struct sway_output *output); +bool sway_session_lock_has_surface(struct sway_session_lock *lock, + struct wlr_surface *surface); void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); #if HAVE_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 4b48a6514..0aae89387 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -41,6 +41,7 @@ struct sway_root { struct wlr_scene_tree *fullscreen; struct wlr_scene_tree *fullscreen_global; struct wlr_scene_tree *seat; + struct wlr_scene_tree *session_lock; } layers; #if HAVE_XWAYLAND diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 321e2a72d..1e4474ce9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -798,6 +798,10 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); + if (server->session_lock.lock) { + sway_session_lock_add_output(server->session_lock.lock, output); + } + struct output_config *oc = find_output_config(output); apply_output_config(oc, output); free_output_config(oc); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 735dddb88..60431d791 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1012,43 +1012,6 @@ void output_render(struct render_context *ctx) { pixman_region32_copy(&transformed_damage, damage); transform_output_damage(&transformed_damage, wlr_output); - if (server.session_lock.locked) { - struct wlr_render_color clear_color = { - .a = 1.0f - }; - if (server.session_lock.lock == NULL) { - // abandoned lock -> red BG - clear_color.r = 1.f; - } - - wlr_render_pass_add_rect(ctx->pass, &(struct wlr_render_rect_options){ - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = clear_color, - .clip = &transformed_damage, - }); - - if (server.session_lock.lock != NULL) { - struct render_data data = { - .alpha = 1.0f, - .ctx = ctx, - }; - - struct wlr_session_lock_surface_v1 *lock_surface; - wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { - if (lock_surface->output != wlr_output) { - continue; - } - if (!lock_surface->surface->mapped) { - continue; - } - - output_surface_for_each_surface(output, lock_surface->surface, - 0.0, 0.0, render_surface_iterator, &data); - } - } - goto renderer_end; - } - if (output_has_opaque_overlay_layer_surface(output)) { goto render_overlay; } @@ -1122,8 +1085,6 @@ render_overlay: &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_layer_popups(ctx, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - -renderer_end: pixman_region32_fini(&transformed_damage); wlr_output_add_software_cursors_to_render_pass(wlr_output, ctx->pass, damage); } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index e8604193d..107424c9a 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -95,24 +95,6 @@ struct sway_node *node_at_coords( double ox = lx, oy = ly; wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); - if (server.session_lock.locked) { - if (server.session_lock.lock == NULL) { - return NULL; - } - struct wlr_session_lock_surface_v1 *lock_surf; - wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) { - if (lock_surf->output != wlr_output) { - continue; - } - - *surface = wlr_surface_surface_at(lock_surf->surface, ox, oy, sx, sy); - if (*surface != NULL) { - return NULL; - } - } - return NULL; - } - // layer surfaces on the overlay layer are rendered on top if ((*surface = layer_surface_at(output, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index dea7a7bdd..b97f0152c 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -405,7 +405,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, char *device_identifier = input_device_get_identifier(wlr_device); bool exact_identifier = keyboard->wlr->group != NULL; seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); - bool locked = server.session_lock.locked; + bool locked = server.session_lock.lock; struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; diff --git a/sway/input/seat.c b/sway/input/seat.c index d2cd1ba4a..b8daa2979 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1060,19 +1060,10 @@ void seat_configure_xcursor(struct sway_seat *seat) { bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { - if (!server.session_lock.locked) { - return true; + if (server.session_lock.lock) { + return sway_session_lock_has_surface(server.session_lock.lock, surface); } - if (server.session_lock.lock == NULL) { - return false; - } - struct wlr_session_lock_surface_v1 *lock_surf; - wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) { - if (lock_surf->surface == surface) { - return true; - } - } - return false; + return true; } static void send_unfocus(struct sway_container *con, void *data) { @@ -1277,8 +1268,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } else { seat_set_workspace_focus(seat, node); } - if (server.session_lock.locked) { - seat_set_focus_surface(seat, server.session_lock.focused, false); + if (server.session_lock.lock) { + seat_set_focus_surface(seat, server.session_lock.lock->focused, false); } } diff --git a/sway/input/switch.c b/sway/input/switch.c index f483cd23e..831f4dbf6 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -34,7 +34,7 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, static void execute_binding(struct sway_switch *sway_switch) { struct sway_seat *seat = sway_switch->seat_device->sway_seat; - bool locked = server.session_lock.locked; + bool locked = server.session_lock.lock; list_t *bindings = config->current_mode->switch_bindings; struct sway_switch_binding *matched_binding = NULL; diff --git a/sway/lock.c b/sway/lock.c index c4fbfe0ea..2856ae676 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include "log.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" @@ -9,19 +10,28 @@ #include "sway/server.h" #include "sway/surface.h" -struct sway_session_lock_surface { - struct wlr_session_lock_surface_v1 *lock_surface; +struct sway_session_lock_output { + struct wlr_scene_tree *tree; + struct wlr_scene_rect *background; + struct sway_session_lock *lock; + struct sway_output *output; - struct wlr_surface *surface; - struct wl_listener map; + + struct wl_list link; // sway_session_lock::outputs + struct wl_listener destroy; - struct wl_listener surface_commit; - struct wl_listener output_commit; - struct wl_listener output_destroy; + struct wl_listener commit; + + struct wlr_session_lock_surface_v1 *surface; + + // invalid if surface is NULL + struct wl_listener surface_destroy; + struct wl_listener surface_map; }; -static void set_lock_focused_surface(struct wlr_surface *focused) { - server.session_lock.focused = focused; +static void focus_surface(struct sway_session_lock *lock, + struct wlr_surface *focused) { + lock->focused = focused; struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { @@ -29,104 +39,189 @@ static void set_lock_focused_surface(struct wlr_surface *focused) { } } +static void refocus_output(struct sway_session_lock_output *output) { + // Move the seat focus to another surface if one is available + if (output->lock->focused == output->surface->surface) { + struct wlr_surface *next_focus = NULL; + + struct sway_session_lock_output *candidate; + wl_list_for_each(candidate, &output->lock->outputs, link) { + if (candidate == output || !candidate->surface) { + continue; + } + + if (candidate->surface->surface->mapped) { + next_focus = candidate->surface->surface; + break; + } + } + + focus_surface(output->lock, next_focus); + } +} + static void handle_surface_map(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); - if (server.session_lock.focused == NULL) { - set_lock_focused_surface(surf->surface); + struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); + if (surf->lock->focused == NULL) { + focus_surface(surf->lock, surf->surface->surface); } cursor_rebase_all(); - surface_enter_output(surf->surface, surf->output); - output_damage_whole(surf->output); } -static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); - output_damage_surface(surf->output, 0, 0, surf->surface, false); +static void handle_surface_destroy(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *output = + wl_container_of(listener, output, surface_destroy); + refocus_output(output); + + sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); + output->surface = NULL; + wl_list_remove(&output->surface_destroy.link); + wl_list_remove(&output->surface_map.link); } -static void handle_output_commit(struct wl_listener *listener, void *data) { +static void lock_output_reconfigure(struct sway_session_lock_output *output) { + int width = output->output->width; + int height = output->output->height; + + wlr_scene_rect_set_size(output->background, width, height); + + if (output->surface) { + wlr_session_lock_surface_v1_configure(output->surface, width, height); + } +} + +static void handle_new_surface(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); + struct wlr_session_lock_surface_v1 *lock_surface = data; + struct sway_output *output = lock_surface->output->data; + + sway_log(SWAY_DEBUG, "new lock layer surface"); + + struct sway_session_lock_output *current_lock_output, *lock_output = NULL; + wl_list_for_each(current_lock_output, &lock->outputs, link) { + if (current_lock_output->output == output) { + lock_output = current_lock_output; + break; + } + } + sway_assert(lock_output, "Couldn't find output to lock"); + sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); + + lock_output->surface = lock_surface; + + wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); + + lock_output->surface_destroy.notify = handle_surface_destroy; + wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); + lock_output->surface_map.notify = handle_surface_map; + wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); + + lock_output_reconfigure(lock_output); +} + +static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { + if (output->surface) { + refocus_output(output); + wl_list_remove(&output->surface_destroy.link); + wl_list_remove(&output->surface_map.link); + } + + wl_list_remove(&output->commit.link); + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->link); + + free(output); +} + +static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *output = + wl_container_of(listener, output, destroy); + sway_session_lock_output_destroy(output); +} + +static void lock_output_handle_commit(struct wl_listener *listener, void *data) { struct wlr_output_event_commit *event = data; - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); + struct sway_session_lock_output *output = + wl_container_of(listener, output, commit); if (event->state->committed & ( WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM)) { - wlr_session_lock_surface_v1_configure(surf->lock_surface, - surf->output->width, surf->output->height); + lock_output_reconfigure(output); } } -static void destroy_lock_surface(struct sway_session_lock_surface *surf) { - // Move the seat focus to another surface if one is available - if (server.session_lock.focused == surf->surface) { - struct wlr_surface *next_focus = NULL; - - struct wlr_session_lock_surface_v1 *other; - wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { - if (other != surf->lock_surface && other->surface->mapped) { - next_focus = other->surface; - break; - } - } - set_lock_focused_surface(next_focus); +static struct sway_session_lock_output *session_lock_output_create( + struct sway_session_lock *lock, struct sway_output *output) { + struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); + if (!lock_output) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output"); + return NULL; } - wl_list_remove(&surf->map.link); - wl_list_remove(&surf->destroy.link); - wl_list_remove(&surf->surface_commit.link); - wl_list_remove(&surf->output_commit.link); - wl_list_remove(&surf->output_destroy.link); - output_damage_whole(surf->output); - free(surf); -} - -static void handle_surface_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); - destroy_lock_surface(surf); -} - -static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = - wl_container_of(listener, surf, output_destroy); - destroy_lock_surface(surf); -} - -static void handle_new_surface(struct wl_listener *listener, void *data) { - struct wlr_session_lock_surface_v1 *lock_surface = data; - struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); - if (surf == NULL) { - return; + struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); + if (!tree) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); + free(lock_output); + return NULL; } - sway_log(SWAY_DEBUG, "new lock layer surface"); + struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ + lock->abandoned ? 1.f : 0.f, + 0.f, + 0.f, + 1.f, + }); + if (!background) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); + wlr_scene_node_destroy(&tree->node); + free(lock_output); + return NULL; + } - struct sway_output *output = lock_surface->output->data; - wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); + lock_output->output = output; + lock_output->tree = tree; + lock_output->background = background; + lock_output->lock = lock; - surf->lock_surface = lock_surface; - surf->surface = lock_surface->surface; - surf->output = output; - surf->map.notify = handle_surface_map; - wl_signal_add(&lock_surface->surface->events.map, &surf->map); - surf->destroy.notify = handle_surface_destroy; - wl_signal_add(&lock_surface->events.destroy, &surf->destroy); - surf->surface_commit.notify = handle_surface_commit; - wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); - surf->output_commit.notify = handle_output_commit; - wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); - surf->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->node.events.destroy, &surf->output_destroy); + lock_output->destroy.notify = lock_node_handle_destroy; + wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); + + lock_output->commit.notify = lock_output_handle_commit; + wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); + + lock_output_reconfigure(lock_output); + + wl_list_insert(&lock->outputs, &lock_output->link); + + return lock_output; +} + +static void sway_session_lock_destroy(struct sway_session_lock* lock) { + struct sway_session_lock_output *lock_output, *tmp_lock_output; + wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { + // destroying the node will also destroy the whole lock output + wlr_scene_node_destroy(&lock_output->tree->node); + } + + if (server.session_lock.lock == lock) { + server.session_lock.lock = NULL; + } + + if (!lock->abandoned) { + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); + } + + free(lock); } static void handle_unlock(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); sway_log(SWAY_DEBUG, "session unlocked"); - server.session_lock.locked = false; - server.session_lock.lock = NULL; - server.session_lock.focused = NULL; - wl_list_remove(&server.session_lock.lock_new_surface.link); - wl_list_remove(&server.session_lock.lock_unlock.link); - wl_list_remove(&server.session_lock.lock_destroy.link); + sway_session_lock_destroy(lock); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { @@ -145,28 +240,22 @@ static void handle_unlock(struct wl_listener *listener, void *data) { struct sway_output *output = root->outputs->items[i]; arrange_layers(output); } - - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } } static void handle_abandon(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); sway_log(SWAY_INFO, "session lock abandoned"); - server.session_lock.lock = NULL; - server.session_lock.focused = NULL; - wl_list_remove(&server.session_lock.lock_new_surface.link); - wl_list_remove(&server.session_lock.lock_unlock.link); - wl_list_remove(&server.session_lock.lock_destroy.link); - - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &lock->outputs, link) { + wlr_scene_rect_set_color(lock_output->background, + (float[4]){ 1.f, 0.f, 0.f, 1.f }); } + + lock->abandoned = true; + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); } static void handle_session_lock(struct wl_listener *listener, void *data) { @@ -174,44 +263,89 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { struct wl_client *client = wl_resource_get_client(lock->resource); if (server.session_lock.lock) { + if (server.session_lock.lock->abandoned) { + sway_log(SWAY_INFO, "Replacing abandoned lock"); + sway_session_lock_destroy(server.session_lock.lock); + } else { + sway_log(SWAY_ERROR, "Cannot lock an already locked session"); + wlr_session_lock_v1_destroy(lock); + return; + } + } + + struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); + if (!sway_lock) { + sway_log(SWAY_ERROR, "failed to allocate a session lock object"); wlr_session_lock_v1_destroy(lock); return; } + wl_list_init(&sway_lock->outputs); + sway_log(SWAY_DEBUG, "session locked"); - server.session_lock.locked = true; - server.session_lock.lock = lock; struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat_unfocus_unless_client(seat, client); } - wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); - wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); - wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + sway_session_lock_add_output(sway_lock, output); + } + + sway_lock->new_surface.notify = handle_new_surface; + wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); + sway_lock->unlock.notify = handle_unlock; + wl_signal_add(&lock->events.unlock, &sway_lock->unlock); + sway_lock->destroy.notify = handle_abandon; + wl_signal_add(&lock->events.destroy, &sway_lock->destroy); wlr_session_lock_v1_send_locked(lock); - - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + server.session_lock.lock = sway_lock; } static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { - assert(server.session_lock.lock == NULL); + // if the server shuts down while a lock is active, destroy the lock + if (server.session_lock.lock) { + sway_session_lock_destroy(server.session_lock.lock); + } + wl_list_remove(&server.session_lock.new_lock.link); wl_list_remove(&server.session_lock.manager_destroy.link); + + server.session_lock.manager = NULL; +} + +void sway_session_lock_add_output(struct sway_session_lock *lock, + struct sway_output *output) { + struct sway_session_lock_output *lock_output = + session_lock_output_create(lock, output); + + // if we run out of memory while trying to lock the screen, the best we + // can do is kill the sway process. Security conscious users will have + // the sway session fall back to a login shell. + if (!lock_output) { + sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); + abort(); + } +} + +bool sway_session_lock_has_surface(struct sway_session_lock *lock, + struct wlr_surface *surface) { + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &lock->outputs, link) { + if (lock_output->surface && lock_output->surface->surface == surface) { + return true; + } + } + + return false; } void sway_session_lock_init(void) { server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); - server.session_lock.lock_new_surface.notify = handle_new_surface; - server.session_lock.lock_unlock.notify = handle_unlock; - server.session_lock.lock_destroy.notify = handle_abandon; server.session_lock.new_lock.notify = handle_session_lock; server.session_lock.manager_destroy.notify = handle_session_lock_destroy; wl_signal_add(&server.session_lock.manager->events.new_lock, diff --git a/sway/tree/output.c b/sway/tree/output.c index 12a2f969e..64ca3d757 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -95,6 +95,7 @@ static void destroy_scene_layers(struct sway_output *output) { wlr_scene_node_destroy(&output->layers.tiling->node); wlr_scene_node_destroy(&output->layers.fullscreen->node); + wlr_scene_node_destroy(&output->layers.session_lock->node); } struct sway_output *output_create(struct wlr_output *wlr_output) { @@ -104,6 +105,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { bool failed = false; output->layers.tiling = alloc_scene_tree(root->staging, &failed); output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); + output->layers.session_lock = alloc_scene_tree(root->staging, &failed); if (!failed) { output->fullscreen_background = wlr_scene_rect_create( diff --git a/sway/tree/root.c b/sway/tree/root.c index 38fcdb7c8..e49415663 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -50,6 +50,7 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); root->layers.seat = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.session_lock = alloc_scene_tree(&root_scene->tree, &failed); if (failed) { wlr_scene_node_destroy(&root_scene->tree.node); From bac3ab552683516ee5a09632468cd8de529d311f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 21 Feb 2022 20:57:45 -0500 Subject: [PATCH 13/36] seat: Remove dead seatop_render function --- include/sway/input/seat.h | 8 -------- sway/desktop/render.c | 9 --------- sway/input/seat.c | 6 ------ 3 files changed, 23 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 3c81a7136..e5aa84783 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -13,7 +13,6 @@ #include "sway/input/text_input.h" struct sway_seat; -struct render_context; struct sway_seatop_impl { void (*button)(struct sway_seat *seat, uint32_t time_msec, @@ -53,7 +52,6 @@ struct sway_seatop_impl { uint32_t time_msec, enum wlr_tablet_tool_tip_state state); void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); - void (*render)(struct sway_seat *seat, struct render_context *ctx); bool allow_set_cursor; }; @@ -353,12 +351,6 @@ void seatop_end(struct sway_seat *seat); */ void seatop_unref(struct sway_seat *seat, struct sway_container *con); -/** - * Instructs a seatop to render anything that it needs to render - * (eg. dropzone for move-tiling) - */ -void seatop_render(struct sway_seat *seat, struct render_context *ctx); - bool seatop_allows_set_cursor(struct sway_seat *seat); /** diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 60431d791..23ced7a15 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -973,13 +973,6 @@ static void render_floating(struct render_context *ctx) { } } -static void render_seatops(struct render_context *ctx) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seatop_render(seat, ctx); - } -} - void output_render(struct render_context *ctx) { struct wlr_output *wlr_output = ctx->output->wlr_output; struct sway_output *output = ctx->output; @@ -1072,8 +1065,6 @@ void output_render(struct render_context *ctx) { &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } - render_seatops(ctx); - struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); if (focus && focus->view) { diff --git a/sway/input/seat.c b/sway/input/seat.c index b8daa2979..75fea484c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1691,12 +1691,6 @@ void seatop_end(struct sway_seat *seat) { seat->seatop_impl = NULL; } -void seatop_render(struct sway_seat *seat, struct render_context *ctx) { - if (seat->seatop_impl->render) { - seat->seatop_impl->render(seat, ctx); - } -} - bool seatop_allows_set_cursor(struct sway_seat *seat) { return seat->seatop_impl->allow_set_cursor; } From 869baff25221e1a1881e9559453faa43f90da33e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 3 Mar 2023 21:13:56 -0500 Subject: [PATCH 14/36] renderer: Remove in favor of scene_graph --- include/sway/output.h | 15 - sway/desktop/render.c | 1081 ----------------------------------------- sway/meson.build | 1 - 3 files changed, 1097 deletions(-) delete mode 100644 sway/desktop/render.c diff --git a/include/sway/output.h b/include/sway/output.h index 8405f78dc..d353ce61a 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -78,14 +78,6 @@ struct sway_output_non_desktop { struct wl_listener destroy; }; -struct render_context { - struct sway_output *output; - struct wlr_renderer *renderer; - const pixman_region32_t *output_damage; - - struct wlr_render_pass *pass; -}; - struct sway_output *output_create(struct wlr_output *wlr_output); void output_destroy(struct sway_output *output); @@ -136,8 +128,6 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output); struct sway_workspace *output_get_active_workspace(struct sway_output *output); -void output_render(struct render_context *ctx); - void output_surface_for_each_surface(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, sway_surface_iterator_func_t iterator, void *user_data); @@ -185,11 +175,6 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct render_context *ctx, const struct wlr_box *_box, - float color[static 4]); - -void premultiply_alpha(float color[4], float opacity); - void scale_box(struct wlr_box *box, float scale); enum wlr_direction opposite_direction(enum wlr_direction d); diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index 23ced7a15..000000000 --- a/sway/desktop/render.c +++ /dev/null @@ -1,1081 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "log.h" -#include "config.h" -#include "sway/config.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" -#include "sway/layers.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/tree/arrange.h" -#include "sway/tree/container.h" -#include "sway/tree/root.h" -#include "sway/tree/view.h" -#include "sway/tree/workspace.h" - -struct render_data { - struct render_context *ctx; - const pixman_region32_t *damage; - float alpha; - struct wlr_box *clip_box; -}; - -static void transform_output_damage(pixman_region32_t *damage, struct wlr_output *output) { - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_region_transform(damage, damage, transform, ow, oh); -} - -static void transform_output_box(struct wlr_box *box, struct wlr_output *output) { - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(box, box, transform, ow, oh); -} - - -/** - * Apply scale to a width or height. - * - * One does not simply multiply the width by the scale. We allow fractional - * scaling, which means the resulting scaled width might be a decimal. - * So we round it. - * - * But even this can produce undesirable results depending on the X or Y offset - * of the box. For example, with a scale of 1.5, a box with width=1 should not - * scale to 2px if its X coordinate is 1, because the X coordinate would have - * scaled to 2px. - */ -static int scale_length(int length, int offset, float scale) { - return roundf((offset + length) * scale) - roundf(offset * scale); -} - -static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { - switch (output->scale_filter) { - case SCALE_FILTER_LINEAR: - return WLR_SCALE_FILTER_BILINEAR; - case SCALE_FILTER_NEAREST: - return WLR_SCALE_FILTER_NEAREST; - default: - abort(); // unreachable - } -} - -static void render_texture(struct render_context *ctx, struct wlr_texture *texture, - const struct wlr_fbox *_src_box, const struct wlr_box *dst_box, - const struct wlr_box *clip_box, enum wl_output_transform transform, float alpha) { - struct sway_output *output = ctx->output; - - struct wlr_box proj_box = *dst_box; - - struct wlr_fbox src_box = {0}; - if (_src_box) { - src_box = *_src_box; - } - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, proj_box.x, proj_box.y, - proj_box.width, proj_box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - - if (clip_box) { - pixman_region32_intersect_rect(&damage, &damage, - clip_box->x, clip_box->y, clip_box->width, clip_box->height); - } - - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - transform_output_box(&proj_box, output->wlr_output); - transform_output_damage(&damage, output->wlr_output); - transform = wlr_output_transform_compose(transform, output->wlr_output->transform); - - wlr_render_pass_add_texture(ctx->pass, &(struct wlr_render_texture_options) { - .texture = texture, - .src_box = src_box, - .dst_box = proj_box, - .transform = transform, - .alpha = &alpha, - .clip = &damage, - .filter_mode = get_scale_filter(output), - }); - -damage_finish: - pixman_region32_fini(&damage); -} - -static void render_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - struct render_data *data = _data; - struct wlr_output *wlr_output = output->wlr_output; - float alpha = data->alpha; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) { - return; - } - - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - - struct wlr_box dst_box = *_box; - struct wlr_box clip_box = *_box; - if (data->clip_box != NULL) { - clip_box.width = fmin(dst_box.width, data->clip_box->width); - clip_box.height = fmin(dst_box.height, data->clip_box->height); - } - scale_box(&dst_box, wlr_output->scale); - scale_box(&clip_box, wlr_output->scale); - - render_texture(data->ctx, texture, - &src_box, &dst_box, &clip_box, surface->current.transform, alpha); - - wlr_presentation_surface_textured_on_output(surface, - wlr_output); -} - -static void render_layer_toplevel(struct render_context *ctx, struct wl_list *layer_surfaces) { - struct render_data data = { - .alpha = 1.0f, - .ctx = ctx, - }; - output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces, - render_surface_iterator, &data); -} - -static void render_layer_popups(struct render_context *ctx, struct wl_list *layer_surfaces) { - struct render_data data = { - .alpha = 1.0f, - .ctx = ctx, - }; - output_layer_for_each_popup_surface(ctx->output, layer_surfaces, - render_surface_iterator, &data); -} - -#if HAVE_XWAYLAND -static void render_unmanaged(struct render_context *ctx, struct wl_list *unmanaged) { - struct render_data data = { - .alpha = 1.0f, - .ctx = ctx, - }; - output_unmanaged_for_each_surface(ctx->output, unmanaged, - render_surface_iterator, &data); -} -#endif - -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_rect(struct render_context *ctx, const struct wlr_box *_box, - float color[static 4]) { - struct wlr_output *wlr_output = ctx->output->wlr_output; - - struct wlr_box box = *_box; - box.x -= ctx->output->lx * wlr_output->scale; - box.y -= ctx->output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - transform_output_damage(&damage, wlr_output); - transform_output_box(&box, wlr_output); - - wlr_render_pass_add_rect(ctx->pass, &(struct wlr_render_rect_options){ - .box = box, - .color = { - .r = color[0], - .g = color[1], - .b = color[2], - .a = color[3], - }, - .clip = &damage, - }); - -damage_finish: - pixman_region32_fini(&damage); -} - -void premultiply_alpha(float color[4], float opacity) { - color[3] *= opacity; - color[0] *= color[3]; - color[1] *= color[3]; - color[2] *= color[3]; -} - -static void render_view_toplevels(struct render_context *ctx, - struct sway_view *view, float alpha) { - struct render_data data = { - .alpha = alpha, - .ctx = ctx, - }; - struct wlr_box clip_box; - if (!container_is_current_floating(view->container)) { - // As we pass the geometry offsets to the surface iterator, we will - // need to account for the offsets in the clip dimensions. - clip_box.width = view->container->current.content_width + view->geometry.x; - clip_box.height = view->container->current.content_height + view->geometry.y; - data.clip_box = &clip_box; - } - // Render all toplevels without descending into popups - double ox = view->container->surface_x - - ctx->output->lx - view->geometry.x; - double oy = view->container->surface_y - - ctx->output->ly - view->geometry.y; - output_surface_for_each_surface(ctx->output, view->surface, ox, oy, - render_surface_iterator, &data); -} - -static void render_view_popups(struct render_context *ctx, struct sway_view *view, - float alpha) { - struct render_data data = { - .alpha = alpha, - .ctx = ctx, - }; - output_view_for_each_popup_surface(ctx->output, view, - render_surface_iterator, &data); -} - -static void render_saved_view(struct render_context *ctx, struct sway_view *view, - float alpha) { - struct sway_output *output = ctx->output; - struct wlr_output *wlr_output = output->wlr_output; - - if (wl_list_empty(&view->saved_buffers)) { - return; - } - - bool floating = container_is_current_floating(view->container); - - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - if (!saved_buf->buffer->texture) { - continue; - } - - struct wlr_box proj_box = { - .x = saved_buf->x - view->saved_geometry.x - output->lx, - .y = saved_buf->y - view->saved_geometry.y - output->ly, - .width = saved_buf->width, - .height = saved_buf->height, - }; - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); - if (!intersects) { - continue; - } - - struct wlr_box dst_box = proj_box; - struct wlr_box clip_box = proj_box; - if (!floating) { - clip_box.width = fmin(dst_box.width, - view->container->current.content_width - - (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); - clip_box.height = fmin(dst_box.height, - view->container->current.content_height - - (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); - } - scale_box(&dst_box, wlr_output->scale); - scale_box(&clip_box, wlr_output->scale); - - render_texture(ctx, saved_buf->buffer->texture, - &saved_buf->source_box, &dst_box, &clip_box, saved_buf->transform, alpha); - } - - // FIXME: we should set the surface that this saved buffer originates from - // as sampled here. - // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 -} - -/** - * Render a view's surface and left/bottom/right borders. - */ -static void render_view(struct render_context *ctx, - struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->view; - if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(ctx, view, view->container->alpha); - } else if (view->surface) { - render_view_toplevels(ctx, view, view->container->alpha); - } - - if (con->current.border == B_NONE || con->current.border == B_CSD) { - return; - } - - struct wlr_box box; - float output_scale = ctx->output->wlr_output->scale; - float color[4]; - struct sway_container_state *state = &con->current; - - if (state->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->content_y); - box.width = state->border_thickness; - box.height = state->content_height; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - } - - list_t *siblings = container_get_current_siblings(con); - enum sway_container_layout layout = - container_current_parent_layout(con); - - if (state->border_right) { - if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = floor(state->content_x + state->content_width); - box.y = floor(state->content_y); - box.width = state->border_thickness; - box.height = state->content_height; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - } - - if (state->border_bottom) { - if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->content_y + state->content_height); - box.width = state->width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - } -} - -/** - * Render a titlebar. - * - * Care must be taken not to render over the same pixel multiple times, - * otherwise the colors will be incorrect when using opacity. - * - * The height is: 1px border, 3px padding, font height, 3px padding, 1px border - * The left side is: 1px border, 2px padding, title - */ -static void render_titlebar(struct render_context *ctx, struct sway_container *con, - int x, int y, int width, - struct border_colors *colors, struct wlr_texture *title_texture, - struct wlr_texture *marks_texture) { - struct wlr_box box; - float color[4]; - struct sway_output *output = ctx->output; - float output_scale = output->wlr_output->scale; - double output_x = output->lx; - double output_y = output->ly; - int titlebar_border_thickness = config->titlebar_border_thickness; - int titlebar_h_padding = config->titlebar_h_padding; - int titlebar_v_padding = config->titlebar_v_padding; - enum alignment title_align = config->title_align; - - // Single pixel bar above title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x; - box.y = y; - box.width = width; - box.height = titlebar_border_thickness; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - // Single pixel bar below title - box.x = x; - box.y = y + container_titlebar_height() - titlebar_border_thickness; - box.width = width; - box.height = titlebar_border_thickness; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - // Single pixel left edge - box.x = x; - box.y = y + titlebar_border_thickness; - box.width = titlebar_border_thickness; - box.height = container_titlebar_height() - titlebar_border_thickness * 2; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - // Single pixel right edge - box.x = x + width - titlebar_border_thickness; - box.y = y + titlebar_border_thickness; - box.width = titlebar_border_thickness; - box.height = container_titlebar_height() - titlebar_border_thickness * 2; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - int inner_x = x - output_x + titlebar_h_padding; - int bg_y = y + titlebar_border_thickness; - size_t inner_width = width - titlebar_h_padding * 2; - - // output-buffer local - int ob_inner_x = roundf(inner_x * output_scale); - int ob_inner_width = scale_length(inner_width, inner_x, output_scale); - int ob_bg_height = scale_length( - (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height, bg_y, output_scale); - - // Marks - int ob_marks_x = 0; // output-buffer-local - int ob_marks_width = 0; // output-buffer-local - if (config->show_marks && marks_texture) { - struct wlr_box texture_box = { - .width = marks_texture->width, - .height = marks_texture->height, - }; - ob_marks_width = texture_box.width; - - // The marks texture might be shorter than the config->font_height, in - // which case we need to pad it as evenly as possible above and below. - int ob_padding_total = ob_bg_height - texture_box.height; - int ob_padding_above = floor(ob_padding_total / 2.0); - int ob_padding_below = ceil(ob_padding_total / 2.0); - - // Render texture. If the title is on the right, the marks will be on - // the left. Otherwise, they will be on the right. - if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { - texture_box.x = ob_inner_x; - } else { - texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; - } - ob_marks_x = texture_box.x; - - texture_box.y = round((bg_y - output_y) * output_scale) + - ob_padding_above; - - struct wlr_box clip_box = texture_box; - if (ob_inner_width < clip_box.width) { - clip_box.width = ob_inner_width; - } - render_texture(ctx, marks_texture, - NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, con->alpha); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = clip_box.x + round(output_x * output_scale); - box.y = roundf((y + titlebar_border_thickness) * output_scale); - box.width = clip_box.width; - box.height = ob_padding_above; - render_rect(ctx, &box, color); - - // Padding below - box.y += ob_padding_above + clip_box.height; - box.height = ob_padding_below; - render_rect(ctx, &box, color); - } - - // Title text - int ob_title_x = 0; // output-buffer-local - int ob_title_width = 0; // output-buffer-local - if (title_texture) { - struct wlr_box texture_box = { - .width = title_texture->width, - .height = title_texture->height, - }; - - // The effective output may be NULL when con is not on any output. - // This can happen because we render all children of containers, - // even those that are out of the bounds of any output. - struct sway_output *effective = container_get_effective_output(con); - float title_scale = effective ? effective->wlr_output->scale : output_scale; - texture_box.width = texture_box.width * output_scale / title_scale; - texture_box.height = texture_box.height * output_scale / title_scale; - ob_title_width = texture_box.width; - - // The title texture might be shorter than the config->font_height, - // in which case we need to pad it above and below. - int ob_padding_above = roundf((titlebar_v_padding - - titlebar_border_thickness) * output_scale); - int ob_padding_below = ob_bg_height - ob_padding_above - - texture_box.height; - - // Render texture - if (texture_box.width > ob_inner_width - ob_marks_width) { - texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) - ? ob_marks_x + ob_marks_width : ob_inner_x; - } else if (title_align == ALIGN_LEFT) { - texture_box.x = ob_inner_x; - } else if (title_align == ALIGN_CENTER) { - // If there are marks visible, center between the edge and marks. - // Otherwise, center in the inner area. - if (ob_marks_width) { - texture_box.x = (ob_inner_x + ob_marks_x) / 2 - - texture_box.width / 2; - } else { - texture_box.x = ob_inner_x + ob_inner_width / 2 - - texture_box.width / 2; - } - } else { - texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; - } - ob_title_x = texture_box.x; - - texture_box.y = - round((bg_y - output_y) * output_scale) + ob_padding_above; - - struct wlr_box clip_box = texture_box; - if (ob_inner_width - ob_marks_width < clip_box.width) { - clip_box.width = ob_inner_width - ob_marks_width; - } - - render_texture(ctx, title_texture, - NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, con->alpha); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = clip_box.x + round(output_x * output_scale); - box.y = roundf((y + titlebar_border_thickness) * output_scale); - box.width = clip_box.width; - box.height = ob_padding_above; - render_rect(ctx, &box, color); - - // Padding below - box.y += ob_padding_above + clip_box.height; - box.height = ob_padding_below; - render_rect(ctx, &box, color); - } - - // Determine the left + right extends of the textures (output-buffer local) - int ob_left_x, ob_left_width, ob_right_x, ob_right_width; - if (ob_title_width == 0 && ob_marks_width == 0) { - ob_left_x = ob_inner_x; - ob_left_width = 0; - ob_right_x = ob_inner_x; - ob_right_width = 0; - } else if (ob_title_x < ob_marks_x) { - ob_left_x = ob_title_x; - ob_left_width = ob_title_width; - ob_right_x = ob_marks_x; - ob_right_width = ob_marks_width; - } else { - ob_left_x = ob_marks_x; - ob_left_width = ob_marks_width; - ob_right_x = ob_title_x; - ob_right_width = ob_title_width; - } - if (ob_left_x < ob_inner_x) { - ob_left_x = ob_inner_x; - } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { - ob_right_x = ob_left_x; - ob_right_width = ob_left_width; - } - - // Filler between title and marks - box.width = ob_right_x - ob_left_x - ob_left_width; - if (box.width > 0) { - box.x = ob_left_x + ob_left_width + round(output_x * output_scale); - box.y = roundf(bg_y * output_scale); - box.height = ob_bg_height; - render_rect(ctx, &box, color); - } - - // Padding on left side - box.x = x + titlebar_border_thickness; - box.y = y + titlebar_border_thickness; - box.width = titlebar_h_padding - titlebar_border_thickness; - box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height; - scale_box(&box, output_scale); - int left_x = ob_left_x + round(output_x * output_scale); - if (box.x + box.width < left_x) { - box.width += left_x - box.x - box.width; - } - render_rect(ctx, &box, color); - - // Padding on right side - box.x = x + width - titlebar_h_padding; - box.y = y + titlebar_border_thickness; - box.width = titlebar_h_padding - titlebar_border_thickness; - box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height; - scale_box(&box, output_scale); - int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); - if (right_rx < box.x) { - box.width += box.x - right_rx; - box.x = right_rx; - } - render_rect(ctx, &box, color); -} - -/** - * Render the top border line for a view using "border pixel". - */ -static void render_top_border(struct render_context *ctx, struct sway_container *con, - struct border_colors *colors) { - struct sway_container_state *state = &con->current; - if (!state->border_top) { - return; - } - struct wlr_box box; - float color[4]; - float output_scale = ctx->output->wlr_output->scale; - - // Child border - top edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->y); - box.width = state->width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); -} - -struct parent_data { - enum sway_container_layout layout; - struct wlr_box box; - list_t *children; - bool focused; - struct sway_container *active_child; -}; - -static void render_container(struct render_context *ctx, - struct sway_container *con, bool parent_focused); - -/** - * Render a container's children using a L_HORIZ or L_VERT layout. - * - * Wrap child views in borders and leave child containers borderless because - * they'll apply their own borders to their children. - */ -static void render_containers_linear(struct render_context *ctx, struct parent_data *parent) { - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - - if (child->view) { - struct sway_view *view = child->view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - struct sway_container_state *state = &child->current; - - if (view_is_urgent(view)) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (state->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - if (state->border == B_NORMAL) { - render_titlebar(ctx, child, floor(state->x), - floor(state->y), state->width, colors, - title_texture, marks_texture); - } else if (state->border == B_PIXEL) { - render_top_border(ctx, child, colors); - } - render_view(ctx, child, colors); - } else { - render_container(ctx, child, - parent->focused || child->current.focused); - } - } -} - -static bool container_is_focused(struct sway_container *con, void *data) { - return con->current.focused; -} - -static bool container_has_focused_child(struct sway_container *con) { - return container_find_child(con, container_is_focused, NULL); -} - -/** - * Render a container's children using the L_TABBED layout. - */ -static void render_containers_tabbed(struct render_context *ctx, struct parent_data *parent) { - if (!parent->children->length) { - return; - } - struct sway_container *current = parent->active_child; - struct border_colors *current_colors = &config->border_colors.unfocused; - int tab_width = parent->box.width / parent->children->length; - - // Render tabs - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - struct sway_view *view = child->view; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - bool urgent = view ? - view_is_urgent(view) : container_has_urgent_child(child); - - if (urgent) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (cstate->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (config->has_focused_tab_title && container_has_focused_child(child)) { - colors = &config->border_colors.focused_tab_title; - title_texture = child->title_focused_tab_title; - marks_texture = child->marks_focused_tab_title; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - int x = floor(cstate->x + tab_width * i); - - // Make last tab use the remaining width of the parent - if (i == parent->children->length - 1) { - tab_width = parent->box.width - tab_width * i; - } - - render_titlebar(ctx, child, x, parent->box.y, tab_width, - colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current->view) { - render_view(ctx, current, current_colors); - } else { - render_container(ctx, current, - parent->focused || current->current.focused); - } -} - -/** - * Render a container's children using the L_STACKED layout. - */ -static void render_containers_stacked(struct render_context *ctx, struct parent_data *parent) { - if (!parent->children->length) { - return; - } - struct sway_container *current = parent->active_child; - struct border_colors *current_colors = &config->border_colors.unfocused; - size_t titlebar_height = container_titlebar_height(); - - // Render titles - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - struct sway_view *view = child->view; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - bool urgent = view ? - view_is_urgent(view) : container_has_urgent_child(child); - - if (urgent) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (cstate->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (config->has_focused_tab_title && container_has_focused_child(child)) { - colors = &config->border_colors.focused_tab_title; - title_texture = child->title_focused_tab_title; - marks_texture = child->marks_focused_tab_title; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - int y = parent->box.y + titlebar_height * i; - render_titlebar(ctx, child, parent->box.x, y, - parent->box.width, colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current->view) { - render_view(ctx, current, current_colors); - } else { - render_container(ctx, current, - parent->focused || current->current.focused); - } -} - -static void render_containers(struct render_context *ctx, struct parent_data *parent) { - if (config->hide_lone_tab && parent->children->length == 1) { - struct sway_container *child = parent->children->items[0]; - if (child->view) { - render_containers_linear(ctx, parent); - return; - } - } - - switch (parent->layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_containers_linear(ctx, parent); - break; - case L_STACKED: - render_containers_stacked(ctx, parent); - break; - case L_TABBED: - render_containers_tabbed(ctx, parent); - break; - } -} - -static void render_container(struct render_context *ctx, - struct sway_container *con, bool focused) { - struct parent_data data = { - .layout = con->current.layout, - .box = { - .x = floor(con->current.x), - .y = floor(con->current.y), - .width = con->current.width, - .height = con->current.height, - }, - .children = con->current.children, - .focused = focused, - .active_child = con->current.focused_inactive_child, - }; - render_containers(ctx, &data); -} - -static void render_workspace(struct render_context *ctx, - struct sway_workspace *ws, bool focused) { - struct parent_data data = { - .layout = ws->current.layout, - .box = { - .x = floor(ws->current.x), - .y = floor(ws->current.y), - .width = ws->current.width, - .height = ws->current.height, - }, - .children = ws->current.tiling, - .focused = focused, - .active_child = ws->current.focused_inactive_child, - }; - render_containers(ctx, &data); -} - -static void render_floating_container(struct render_context *ctx, - struct sway_container *con) { - if (con->view) { - struct sway_view *view = con->view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (view_is_urgent(view)) { - colors = &config->border_colors.urgent; - title_texture = con->title_urgent; - marks_texture = con->marks_urgent; - } else if (con->current.focused) { - colors = &config->border_colors.focused; - title_texture = con->title_focused; - marks_texture = con->marks_focused; - } else { - colors = &config->border_colors.unfocused; - title_texture = con->title_unfocused; - marks_texture = con->marks_unfocused; - } - - if (con->current.border == B_NORMAL) { - render_titlebar(ctx, con, floor(con->current.x), - floor(con->current.y), con->current.width, colors, - title_texture, marks_texture); - } else if (con->current.border == B_PIXEL) { - render_top_border(ctx, con, colors); - } - render_view(ctx, con, colors); - } else { - render_container(ctx, con, con->current.focused); - } -} - -static void render_floating(struct render_context *ctx) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->current.workspaces->length; ++j) { - struct sway_workspace *ws = output->current.workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - for (int k = 0; k < ws->current.floating->length; ++k) { - struct sway_container *floater = ws->current.floating->items[k]; - if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { - continue; - } - render_floating_container(ctx, floater); - } - } - } -} - -void output_render(struct render_context *ctx) { - struct wlr_output *wlr_output = ctx->output->wlr_output; - struct sway_output *output = ctx->output; - const pixman_region32_t *damage = ctx->output_damage; - - struct sway_workspace *workspace = output->current.active_workspace; - if (workspace == NULL) { - return; - } - - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - fullscreen_con = workspace->current.fullscreen; - } - - if (!pixman_region32_not_empty(damage)) { - // Output isn't damaged but needs buffer swap - return; - } - - if (debug.damage == DAMAGE_HIGHLIGHT) { - wlr_render_pass_add_rect(ctx->pass, &(struct wlr_render_rect_options){ - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = { .r = 1, .g = 1, .b = 0, .a = 1 }, - }); - } - - pixman_region32_t transformed_damage; - pixman_region32_init(&transformed_damage); - pixman_region32_copy(&transformed_damage, damage); - transform_output_damage(&transformed_damage, wlr_output); - - if (output_has_opaque_overlay_layer_surface(output)) { - goto render_overlay; - } - - if (fullscreen_con) { - wlr_render_pass_add_rect(ctx->pass, &(struct wlr_render_rect_options){ - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, - .clip = &transformed_damage, - }); - - if (fullscreen_con->view) { - if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { - render_saved_view(ctx, fullscreen_con->view, 1.0f); - } else if (fullscreen_con->view->surface) { - render_view_toplevels(ctx, fullscreen_con->view, 1.0f); - } - } else { - render_container(ctx, fullscreen_con, - fullscreen_con->current.focused); - } - - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, fullscreen_con)) { - render_floating_container(ctx, floater); - } - } -#if HAVE_XWAYLAND - render_unmanaged(ctx, &root->xwayland_unmanaged); -#endif - } else { - wlr_render_pass_add_rect(ctx->pass, &(struct wlr_render_rect_options){ - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = { .r = 0.25f, .g = 0.25f, .b = 0.25f, .a = 1 }, - .clip = &transformed_damage, - }); - - render_layer_toplevel(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - render_workspace(ctx, workspace, workspace->current.focused); - render_floating(ctx); -#if HAVE_XWAYLAND - render_unmanaged(ctx, &root->xwayland_unmanaged); -#endif - render_layer_toplevel(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - - render_layer_popups(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - } - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - if (focus && focus->view) { - render_view_popups(ctx, focus->view, focus->alpha); - } - -render_overlay: - render_layer_toplevel(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(ctx, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - pixman_region32_fini(&transformed_damage); - wlr_output_add_software_cursors_to_render_pass(wlr_output, ctx->pass, damage); -} diff --git a/sway/meson.build b/sway/meson.build index 26251e586..04b0dd93e 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -18,7 +18,6 @@ sway_sources = files( 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', - 'desktop/render.c', 'desktop/surface.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', From 946fc8094559801bc4be629368ac31025d28165a Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 23 Nov 2023 10:08:28 -0500 Subject: [PATCH 15/36] Introduce sway_text_node This is a helper on top of a wlr_scene_buffer that will handle text rendering for us. --- include/sway/sway_text_node.h | 28 ++++ sway/meson.build | 1 + sway/sway_text_node.c | 303 ++++++++++++++++++++++++++++++++++ 3 files changed, 332 insertions(+) create mode 100644 include/sway/sway_text_node.h create mode 100644 sway/sway_text_node.c diff --git a/include/sway/sway_text_node.h b/include/sway/sway_text_node.h new file mode 100644 index 000000000..0d4209bb6 --- /dev/null +++ b/include/sway/sway_text_node.h @@ -0,0 +1,28 @@ +#ifndef _SWAY_BUFFER_H +#define _SWAY_BUFFER_H +#include + +struct sway_text_node { + int width; + int max_width; + int height; + int baseline; + bool pango_markup; + float color[4]; + float background[4]; + + struct wlr_scene_node *node; +}; + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, float color[4], bool pango_markup); + +void sway_text_node_set_color(struct sway_text_node *node, float color[4]); + +void sway_text_node_set_text(struct sway_text_node *node, char *text); + +void sway_text_node_set_max_width(struct sway_text_node *node, int max_width); + +void sway_text_node_set_background(struct sway_text_node *node, float background[4]); + +#endif diff --git a/sway/meson.build b/sway/meson.build index 04b0dd93e..110de58c3 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -10,6 +10,7 @@ sway_sources = files( 'realtime.c', 'scene_descriptor.c', 'server.c', + 'sway_text_node.c', 'swaynag.c', 'xdg_activation_v1.c', 'xdg_decoration.c', diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c new file mode 100644 index 000000000..b9a77d94d --- /dev/null +++ b/sway/sway_text_node.c @@ -0,0 +1,303 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include "cairo_util.h" +#include "log.h" +#include "pango.h" +#include "sway/config.h" +#include "sway/sway_text_node.h" + +struct cairo_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; + cairo_t *cairo; +}; + +static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + + cairo_surface_destroy(buffer->surface); + cairo_destroy(buffer->cairo); + free(buffer); +} + +static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, uint32_t *format, size_t *stride) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + *data = cairo_image_surface_get_data(buffer->surface); + *stride = cairo_image_surface_get_stride(buffer->surface); + *format = DRM_FORMAT_ARGB8888; + return true; +} + +static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // This space is intentionally left blank +} + +static const struct wlr_buffer_impl cairo_buffer_impl = { + .destroy = cairo_buffer_handle_destroy, + .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access, + .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access, +}; + +struct text_buffer { + struct wlr_scene_buffer *buffer_node; + char *text; + struct sway_text_node props; + + bool visible; + float scale; + enum wl_output_subpixel subpixel; + + struct wl_listener outputs_update; + struct wl_listener destroy; +}; + +static int get_text_width(struct sway_text_node *props) { + if (props->max_width) { + return MIN(props->max_width, props->width); + } + + return props->width; +} + +static void update_source_box(struct text_buffer *buffer) { + struct sway_text_node *props = &buffer->props; + struct wlr_fbox source_box = { + .x = 0, + .y = 0, + .width = ceil(get_text_width(props) * buffer->scale), + .height = ceil(props->height * buffer->scale), + }; + + wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); +} + +static void render_backing_buffer(struct text_buffer *buffer) { + if (!buffer->visible) { + return; + } + + float scale = buffer->scale; + int width = ceil(buffer->props.width * scale); + int height = ceil(buffer->props.height * scale); + float *color = (float *)&buffer->props.color; + float *background = (float *)&buffer->props.background; + PangoContext *pango = NULL; + + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); + enum wl_output_subpixel subpixel = buffer->subpixel; + if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); + } else { + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel)); + } + + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, width, height); + cairo_status_t status = cairo_surface_status(surface); + if (status != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", + cairo_status_to_string(status)); + goto err; + } + + struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer)); + if (!cairo_buffer) { + sway_log(SWAY_ERROR, "cairo_buffer allocation failed"); + goto err; + } + + cairo_t *cairo = cairo_create(surface); + if (!cairo) { + sway_log(SWAY_ERROR, "cairo_create failed"); + free(cairo_buffer); + goto err; + } + + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_set_font_options(cairo, fo); + pango = pango_cairo_create_context(cairo); + + cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]); + cairo_rectangle(cairo, 0, 0, width, height); + cairo_fill(cairo); + + cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); + cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale); + + render_text(cairo, config->font_description, scale, buffer->props.pango_markup, + "%s", buffer->text); + + cairo_surface_flush(surface); + + wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height); + cairo_buffer->surface = surface; + cairo_buffer->cairo = cairo; + + wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); + wlr_buffer_drop(&cairo_buffer->base); + update_source_box(buffer); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + if (background[3] == 1) { + pixman_region32_union_rect(&opaque, &opaque, 0, 0, + buffer->props.width, buffer->props.height); + } + wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); + pixman_region32_fini(&opaque); + +err: + if (pango) g_object_unref(pango); + cairo_font_options_destroy(fo); +} + +static void handle_outputs_update(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update); + struct wlr_scene_outputs_update_event *event = data; + + float scale = 0; + enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + + for (size_t i = 0; i < event->size; i++) { + struct wlr_scene_output *output = event->active[i]; + if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { + subpixel = output->output->subpixel; + } else if (subpixel != output->output->subpixel) { + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } + + if (scale != 0 && scale != output->output->scale) { + // drop down to gray scale if we encounter outputs with different + // scales or else we will have chromatic aberations + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } + + if (scale < output->output->scale) { + scale = output->output->scale; + } + } + + buffer->visible = event->size > 0; + + if (scale != buffer->scale || subpixel != buffer->subpixel) { + buffer->scale = scale; + buffer->subpixel = subpixel; + render_backing_buffer(buffer); + } +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, destroy); + + wl_list_remove(&buffer->outputs_update.link); + wl_list_remove(&buffer->destroy.link); + + free(buffer->text); + free(buffer); +} + +static void text_calc_size(struct text_buffer *buffer) { + struct sway_text_node *props = &buffer->props; + + cairo_t *c = cairo_create(NULL); + if (!c) { + sway_log(SWAY_ERROR, "cairo_t allocation failed"); + return; + } + + cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); + get_text_size(c, config->font_description, &props->width, NULL, + &props->baseline, 1, props->pango_markup, "%s", buffer->text); + cairo_destroy(c); + + wlr_scene_buffer_set_dest_size(buffer->buffer_node, + get_text_width(props), props->height); +} + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, float color[4], bool pango_markup) { + struct text_buffer *buffer = calloc(1, sizeof(*buffer)); + if (!buffer) { + return NULL; + } + + struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL); + if (!node) { + free(buffer); + return NULL; + } + + buffer->buffer_node = node; + buffer->props.node = &node->node; + buffer->text = strdup(text); + if (!buffer->text) { + free(buffer); + wlr_scene_node_destroy(&node->node); + return NULL; + } + + buffer->props.height = config->font_height; + buffer->props.pango_markup = pango_markup; + memcpy(&buffer->props.color, color, sizeof(*color) * 4); + + buffer->destroy.notify = handle_destroy; + wl_signal_add(&node->node.events.destroy, &buffer->destroy); + buffer->outputs_update.notify = handle_outputs_update; + wl_signal_add(&node->events.outputs_update, &buffer->outputs_update); + + text_calc_size(buffer); + + return &buffer->props; +} + +void sway_text_node_set_color(struct sway_text_node *node, float color[4]) { + if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) { + return; + } + + memcpy(&node->color, color, sizeof(*color) * 4); + struct text_buffer *buffer = wl_container_of(node, buffer, props); + + render_backing_buffer(buffer); +} + +void sway_text_node_set_text(struct sway_text_node *node, char *text) { + struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (strcmp(buffer->text, text) == 0) { + return; + } + + char *new_text = strdup(text); + if (!new_text) { + return; + } + + free(buffer->text); + buffer->text = new_text; + + text_calc_size(buffer); + render_backing_buffer(buffer); +} + +void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { + struct text_buffer *buffer = wl_container_of(node, buffer, props); + buffer->props.max_width = max_width; + wlr_scene_buffer_set_dest_size(buffer->buffer_node, + get_text_width(&buffer->props), buffer->props.height); + update_source_box(buffer); + render_backing_buffer(buffer); +} + +void sway_text_node_set_background(struct sway_text_node *node, float background[4]) { + struct text_buffer *buffer = wl_container_of(node, buffer, props); + memcpy(&node->background, background, sizeof(*background) * 4); + render_backing_buffer(buffer); +} From 5b8b505af5d5925ae9e617ee8f3c7a0f9c43409d Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 23 Nov 2023 10:09:48 -0500 Subject: [PATCH 16/36] input: Query scene graph for relevant surface/node intersections --- include/sway/scene_descriptor.h | 3 + include/sway/tree/container.h | 13 -- include/sway/tree/root.h | 4 + sway/input/cursor.c | 194 ++++++++--------------- sway/tree/container.c | 265 +------------------------------- sway/tree/root.c | 19 ++- sway/tree/view.c | 7 + 7 files changed, 93 insertions(+), 412 deletions(-) diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 057993ec6..8af812192 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -12,6 +12,9 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_NON_INTERACTIVE, + SWAY_SCENE_DESC_CONTAINER, + SWAY_SCENE_DESC_VIEW, SWAY_SCENE_DESC_DRAG_ICON, }; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ee22a0d02..6f72439cb 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -172,19 +172,6 @@ void container_begin_destroy(struct sway_container *con); struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data); -/** - * Find a container at the given coordinates. Returns the surface and - * surface-local coordinates of the given layout coordinates if the container - * is a view and the view contains a surface at those coordinates. - */ -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy); - -struct sway_container *tiling_container_at( - struct sway_node *parent, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 0aae89387..003606aab 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -35,6 +35,10 @@ struct sway_root { // staging node will be visible. struct wlr_scene_tree *staging; + // tree containing all layers the compositor will render. Cursor handling + // will end up iterating this tree. + struct wlr_scene_tree *layer_tree; + struct { struct wlr_scene_tree *tiling; struct wlr_scene_tree *floating; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 107424c9a..79373e408 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -37,43 +37,6 @@ static uint32_t get_current_time_msec(void) { return now.tv_sec * 1000 + now.tv_nsec / 1000000; } -static struct wlr_surface *layer_surface_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub) { - return sub; - } - } - return NULL; -} - -static bool surface_is_xdg_popup(struct wlr_surface *surface) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_try_from_wlr_surface(surface); - return xdg_surface != NULL && xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && - xdg_surface->popup != NULL; -} - -static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub && surface_is_xdg_popup(sub)) { - return sub; - } - } - return NULL; -} - /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). @@ -81,119 +44,82 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - // find the output the cursor is on + struct wlr_scene_node *scene_node = NULL; + + struct wlr_scene_node *node; + wl_list_for_each_reverse(node, &root->layer_tree->children, link) { + struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); + + bool non_interactive = scene_descriptor_try_get(&layer->node, + SWAY_SCENE_DESC_NON_INTERACTIVE); + if (non_interactive) { + continue; + } + + scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); + if (scene_node) { + break; + } + } + + if (scene_node) { + // determine what wlr_surface we clicked on + if (scene_node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(scene_node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + + if (scene_surface) { + *surface = scene_surface->surface; + } + } + + // determine what container we clicked on + struct wlr_scene_node *current = scene_node; + while (true) { + struct sway_container *con = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_CONTAINER); + if (!con) { + struct sway_view *view = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_VIEW); + if (view) { + con = view->container; + } + } + + if (con) { + if (!con->view || con->view->surface) { + return &con->node; + } + } + + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } + } + + // if we aren't on a container, determine what workspace we are on struct wlr_output *wlr_output = wlr_output_layout_output_at( root->output_layout, lx, ly); if (wlr_output == NULL) { return NULL; } + struct sway_output *output = wlr_output->data; if (!output || !output->enabled) { // output is being destroyed or is being enabled return NULL; } - double ox = lx, oy = ly; - wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); - // layer surfaces on the overlay layer are rendered on top - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - ox, oy, sx, sy))) { - return NULL; - } - - // check for unmanaged views -#if HAVE_XWAYLAND - struct wl_list *unmanaged = &root->xwayland_unmanaged; - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - - double _sx = lx - unmanaged_surface->lx; - double _sy = ly - unmanaged_surface->ly; - if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { - *surface = xsurface->surface; - *sx = _sx; - *sy = _sy; - return NULL; - } - } -#endif - - if (root->fullscreen_global) { - // Try fullscreen container - struct sway_container *con = tiling_container_at( - &root->fullscreen_global->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - - // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); if (!ws) { return NULL; } - if (ws->fullscreen) { - // Try transient containers - for (int i = 0; i < ws->floating->length; ++i) { - struct sway_container *floater = ws->floating->items[i]; - if (container_is_transient_for(floater, ws->fullscreen)) { - struct sway_container *con = tiling_container_at( - &floater->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - } - } - // Try fullscreen container - struct sway_container *con = - tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - - struct sway_container *c; - if ((c = container_at(ws, lx, ly, surface, sx, sy))) { - return &c->node; - } - - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - return &ws->node; } diff --git a/sway/tree/container.c b/sway/tree/container.c index 307bf9633..8fca6a12d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -16,6 +16,7 @@ #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/scene_descriptor.h" #include "sway/output.h" #include "sway/server.h" #include "sway/surface.h" @@ -94,6 +95,11 @@ struct sway_container *container_create(struct sway_view *view) { c->border.right = alloc_rect_node(c->border.tree, &failed); } + if (!failed && !scene_descriptor_assign(&c->scene_tree->node, + SWAY_SCENE_DESC_CONTAINER, c)) { + failed = true; + } + if (failed) { wlr_scene_node_destroy(&c->scene_tree->node); free(c); @@ -239,265 +245,6 @@ struct sway_container *container_find_child(struct sway_container *container, return NULL; } -static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(con->view, "Expected a view")) { - return NULL; - } - struct sway_view *view = con->view; - double view_sx = lx - con->surface_x + view->geometry.x; - double view_sy = ly - con->surface_y + view->geometry.y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - switch (view->type) { -#if HAVE_XWAYLAND - case SWAY_VIEW_XWAYLAND: - _surface = wlr_surface_surface_at(view->surface, - view_sx, view_sy, &_sx, &_sy); - break; -#endif - case SWAY_VIEW_XDG_SHELL: - _surface = wlr_xdg_surface_surface_at( - view->wlr_xdg_toplevel->base, - view_sx, view_sy, &_sx, &_sy); - break; - } - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return con; - } - return NULL; -} - -/** - * container_at for a container with layout L_TABBED. - */ -static struct sway_container *container_at_tabbed(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - if (!children->length) { - return NULL; - } - - // Tab titles - int title_height = container_titlebar_height(); - if (ly < box.y + title_height) { - int tab_width = box.width / children->length; - int child_index = (lx - box.x) / tab_width; - if (child_index >= children->length) { - child_index = children->length - 1; - } - struct sway_container *child = children->items[child_index]; - return child; - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_STACKED. - */ -static struct sway_container *container_at_stacked(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - - // Title bars - int title_height = container_titlebar_height(); - if (title_height > 0) { - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; - } - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_HORIZ or L_VERT. - */ -static struct sway_container *container_at_linear(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - list_t *children = node_get_children(parent); - for (int i = 0; i < children->length; ++i) { - struct sway_container *child = children->items[i]; - struct sway_container *container = - tiling_container_at(&child->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - return NULL; -} - -static struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - // For outputs with floating containers that overhang the output bounds, - // those at the end of the output list appear on top of floating - // containers from other outputs, so iterate the list in reverse. - for (int i = root->outputs->length - 1; i >= 0; --i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->workspaces->length; ++j) { - struct sway_workspace *ws = output->workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - // Items at the end of the list are on top, so iterate the list in - // reverse. - for (int k = ws->floating->length - 1; k >= 0; --k) { - struct sway_container *floater = ws->floating->items[k]; - struct sway_container *container = - tiling_container_at(&floater->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - } - } - return NULL; -} - -static struct sway_container *view_container_content_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.content_x, - .y = container->pending.content_y, - .width = container->pending.content_width, - .height = container->pending.content_height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -static struct sway_container *view_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.x, - .y = container->pending.y, - .width = container->pending.width, - .height = container->pending.height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -struct sway_container *tiling_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (node_is_view(parent)) { - return view_container_at(parent, lx, ly, surface, sx, sy); - } - if (!node_get_children(parent)) { - return NULL; - } - switch (node_get_layout(parent)) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_NONE: - return NULL; - } - return NULL; -} - -static bool surface_is_popup(struct wlr_surface *surface) { - while (wlr_xdg_surface_try_from_wlr_surface(surface) == NULL) { - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(surface); - if (subsurface == NULL) { - return false; - } - surface = subsurface->parent; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_try_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL; -} - -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct sway_container *c; - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - bool is_floating = focus && container_is_floating_or_child(focus); - // Focused view's popups - if (focus && focus->view) { - c = surface_at_view(focus, lx, ly, surface, sx, sy); - if (c && surface_is_popup(*surface)) { - return c; - } - *surface = NULL; - } - // Floating - if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { - return c; - } - // Tiling (focused) - if (focus && focus->view && !is_floating) { - if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { - return c; - } - } - // Tiling (non-focused) - if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { - return c; - } - return NULL; -} - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { diff --git a/sway/tree/root.c b/sway/tree/root.c index e49415663..fbdd9a966 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -9,6 +9,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -44,13 +45,19 @@ struct sway_root *root_create(struct wl_display *wl_display) { bool failed = false; root->staging = alloc_scene_tree(&root_scene->tree, &failed); + root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.tiling = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.floating = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.seat = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.session_lock = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); + root->layers.floating = 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.seat = alloc_scene_tree(root->layer_tree, &failed); + root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); + + if (!failed && !scene_descriptor_assign(&root->layers.seat->node, + SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { + failed = true; + } if (failed) { wlr_scene_node_destroy(&root_scene->tree.node); diff --git a/sway/tree/view.c b/sway/tree/view.c index bc968edc9..d349e5faf 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -24,6 +24,7 @@ #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/input/seat.h" +#include "sway/scene_descriptor.h" #include "sway/server.h" #include "sway/surface.h" #include "sway/tree/arrange.h" @@ -40,6 +41,12 @@ bool view_init(struct sway_view *view, enum sway_view_type type, bool failed = false; view->scene_tree = alloc_scene_tree(root->staging, &failed); view->content_tree = alloc_scene_tree(view->scene_tree, &failed); + + if (!failed && !scene_descriptor_assign(&view->scene_tree->node, + SWAY_SCENE_DESC_VIEW, view)) { + failed = true; + } + if (failed) { wlr_scene_node_destroy(&view->scene_tree->node); return false; From 188811f80861caacd016b857b0d07f6d2d62d15a Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 18 Jan 2024 10:04:51 -0500 Subject: [PATCH 17/36] 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 | 724 ++++++++++---------------------- sway/desktop/output.c | 85 ---- sway/input/cursor.c | 6 + sway/tree/output.c | 13 +- sway/tree/root.c | 5 + 9 files changed, 258 insertions(+), 644 deletions(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index 9220bdb54..a7afb9001 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 wlr_scene_tree *scene; + struct sway_layer_surface *toplevel; + 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 wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; 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 d353ce61a..ea5d8f47e 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 8af812192..970adaa5b 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 003606aab..2f717baea 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 6480d7ce4..a52d27fa5 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); + 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 (memcmp(&usable_area, &output->usable_area, - sizeof(struct wlr_box)) != 0) { + 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; - } - } - - 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); - } - } +} + +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; + } + + 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; + struct sway_layer_surface *surface = + wl_container_of(listener, surface, surface_commit); - if (layer_surface->initial_commit) { - surface_enter_output(layer_surface->surface, output); - } else if (!layer_surface->initialized) { + 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(); -} - -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); - } - - 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); + 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 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); + 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); + } + 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 popup_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_popup *popup = + wl_container_of(listener, popup, destroy); -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_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; - 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; + // if a client tries to create a popup while we are in the process of destroying + // its output, don't crash. + if (!output) { + return; + } + + 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; + sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); + return; + } + + 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; + } + + surface->output = output; + + surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, - &sway_layer->surface_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); - 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); + surface->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.disable, &surface->output_destroy); - sway_layer->layer_surface = layer_surface; - layer_surface->data = sway_layer; - - 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); - - wl_list_insert(&output->shell_layers[layer_surface->pending.layer], - &sway_layer->link); + 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 1e4474ce9..942bc780b 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 79373e408..30df76f45 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 64ca3d757..3c8823dc9 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 fbdd9a966..7c8f9ea69 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); From 6d7b1321db54155cf78305dbafdcfc7de9b78415 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 21 Nov 2023 19:51:57 -0500 Subject: [PATCH 18/36] scene_graph: Port container server side decorations --- include/sway/tree/container.h | 37 +-- sway/commands/client.c | 7 +- sway/commands/mark.c | 2 +- sway/commands/reload.c | 7 +- sway/commands/show_marks.c | 6 +- sway/commands/title_align.c | 6 + sway/commands/unmark.c | 11 +- sway/tree/container.c | 545 +++++++++++++++++++++------------- sway/tree/view.c | 10 +- 9 files changed, 378 insertions(+), 253 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 6f72439cb..4920e064b 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -76,6 +76,9 @@ struct sway_container { struct wlr_scene_tree *border; struct wlr_scene_tree *background; + + struct sway_text_node *title_text; + struct sway_text_node *marks_text; } title_bar; struct { @@ -94,6 +97,7 @@ struct sway_container { char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar + int title_width; enum sway_container_layout prev_split_layout; @@ -141,18 +145,7 @@ struct sway_container { float alpha; - struct wlr_texture *title_focused; - struct wlr_texture *title_focused_inactive; - struct wlr_texture *title_focused_tab_title; - struct wlr_texture *title_unfocused; - struct wlr_texture *title_urgent; - list_t *marks; // char * - struct wlr_texture *marks_focused; - struct wlr_texture *marks_focused_inactive; - struct wlr_texture *marks_focused_tab_title; - struct wlr_texture *marks_unfocused; - struct wlr_texture *marks_urgent; struct { struct wl_signal destroy; @@ -194,7 +187,9 @@ void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); -void container_update_title_textures(struct sway_container *container); +void container_update_title_bar(struct sway_container *container); + +void container_update_marks(struct sway_container *container); size_t container_build_representation(enum sway_container_layout layout, list_t *children, char *buffer); @@ -230,11 +225,6 @@ void container_set_geometry_from_content(struct sway_container *con); */ bool container_is_floating(struct sway_container *container); -/** - * Same as above, but for current container state. - */ -bool container_is_current_floating(struct sway_container *container); - /** * Get a container's box in layout coordinates. */ @@ -308,15 +298,10 @@ void container_discover_outputs(struct sway_container *con); enum sway_container_layout container_parent_layout(struct sway_container *con); -enum sway_container_layout container_current_parent_layout( - struct sway_container *con); - list_t *container_get_siblings(struct sway_container *container); int container_sibling_index(struct sway_container *child); -list_t *container_get_current_siblings(struct sway_container *container); - void container_handle_fullscreen_reparent(struct sway_container *con); void container_add_child(struct sway_container *parent, @@ -364,8 +349,6 @@ bool container_has_mark(struct sway_container *container, char *mark); void container_add_mark(struct sway_container *container, char *mark); -void container_update_marks_textures(struct sway_container *container); - void container_raise_floating(struct sway_container *con); bool container_is_scratchpad_hidden(struct sway_container *con); @@ -389,4 +372,10 @@ bool container_is_sticky_or_child(struct sway_container *con); */ int container_squash(struct sway_container *con); +void container_arrange_title_bar(struct sway_container *con); + +void container_update(struct sway_container *con); + +void container_update_itself_and_parents(struct sway_container *con); + #endif diff --git a/sway/commands/client.c b/sway/commands/client.c index 772631456..7a9cff2c7 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -5,9 +5,8 @@ #include "sway/tree/container.h" #include "util.h" -static void rebuild_textures_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); - container_update_title_textures(con); +static void container_update_iterator(struct sway_container *con, void *data) { + container_update(con); } static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, @@ -51,7 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, memcpy(class, &colors, sizeof(struct border_colors)); if (config->active) { - root_for_each_container(rebuild_textures_iterator, NULL); + root_for_each_container(container_update_iterator, NULL); for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; diff --git a/sway/commands/mark.c b/sway/commands/mark.c index aa5f185c8..30cf458c7 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -59,7 +59,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } free(mark); - container_update_marks_textures(container); + container_update_marks(container); if (container->view) { view_execute_criteria(container->view); } diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 76f14bba3..82967ca7f 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -9,9 +9,8 @@ #include "list.h" #include "log.h" -static void rebuild_textures_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); - container_update_title_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_title_bar(con); } static void do_reload(void *data) { @@ -48,7 +47,7 @@ static void do_reload(void *data) { } list_free_items_and_destroy(bar_ids); - root_for_each_container(rebuild_textures_iterator, NULL); + root_for_each_container(title_bar_update_iterator, NULL); arrange_root(); } diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 0d373b80c..f738144fe 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -10,8 +10,8 @@ #include "stringop.h" #include "util.h" -static void rebuild_marks_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_marks(con); } struct cmd_results *cmd_show_marks(int argc, char **argv) { @@ -23,7 +23,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { - root_for_each_container(rebuild_marks_iterator, NULL); + root_for_each_container(title_bar_update_iterator, NULL); } for (int i = 0; i < root->outputs->length; ++i) { diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index c30355deb..7f5dd3ae5 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c @@ -4,6 +4,10 @@ #include "sway/tree/container.h" #include "sway/tree/root.h" +static void arrange_title_bar_iterator(struct sway_container *con, void *data) { + container_arrange_title_bar(con); +} + struct cmd_results *cmd_title_align(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { @@ -21,6 +25,8 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { "Expected 'title_align left|center|right'"); } + root_for_each_container(arrange_title_bar_iterator, NULL); + for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; output_damage_whole(output); diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 19274dfbf..c3a6ac4b9 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c @@ -8,9 +8,13 @@ #include "log.h" #include "stringop.h" -static void remove_all_marks_iterator(struct sway_container *con, void *data) { +static void remove_mark(struct sway_container *con) { container_clear_marks(con); - container_update_marks_textures(con); + container_update_marks(con); +} + +static void remove_all_marks_iterator(struct sway_container *con, void *data) { + remove_mark(con); } // unmark Remove all marks from all views @@ -38,8 +42,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { } } else if (con && !mark) { // Clear all marks from the given container - container_clear_marks(con); - container_update_marks_textures(con); + remove_mark(con); } else if (!con && mark) { // Remove mark from whichever container has it container_find_and_unmark(mark); diff --git a/sway/tree/container.c b/sway/tree/container.c index 8fca6a12d..4aae4d327 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -8,15 +8,13 @@ #include #include #include "linux-dmabuf-unstable-v1-protocol.h" -#include "cairo_util.h" -#include "pango.h" #include "sway/config.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/scene_descriptor.h" +#include "sway/sway_text_node.h" #include "sway/output.h" #include "sway/server.h" #include "sway/surface.h" @@ -120,9 +118,328 @@ struct sway_container *container_create(struct sway_view *view) { wl_signal_init(&c->events.destroy); wl_signal_emit_mutable(&root->events.new_node, &c->node); + container_update(c); + return c; } +static bool container_is_focused(struct sway_container *con, void *data) { + return con->current.focused; +} + +static bool container_has_focused_child(struct sway_container *con) { + return container_find_child(con, container_is_focused, NULL); +} + +static bool container_is_current_parent_focused(struct sway_container *con) { + if (con->current.parent) { + struct sway_container *parent = con->current.parent; + return parent->current.focused || container_is_current_parent_focused(parent); + } else if (con->current.workspace) { + struct sway_workspace *ws = con->current.workspace; + return ws->current.focused; + } + + return false; +} + +static struct border_colors *container_get_current_colors( + struct sway_container *con) { + struct border_colors *colors; + + bool urgent = con->view ? + view_is_urgent(con->view) : container_has_urgent_child(con); + struct sway_container *active_child; + + if (con->current.parent) { + active_child = con->current.parent->current.focused_inactive_child; + } else if (con->current.workspace) { + active_child = con->current.workspace->current.focused_inactive_child; + } else { + active_child = NULL; + } + + if (urgent) { + colors = &config->border_colors.urgent; + } else if (con->current.focused || container_is_current_parent_focused(con)) { + colors = &config->border_colors.focused; + } else if (config->has_focused_tab_title && container_has_focused_child(con)) { + colors = &config->border_colors.focused_tab_title; + } else if (con == active_child) { + colors = &config->border_colors.focused_inactive; + } else { + colors = &config->border_colors.unfocused; + } + + return colors; +} + +static bool container_is_current_floating(struct sway_container *container) { + if (!container->current.parent && container->current.workspace && + list_find(container->current.workspace->floating, container) != -1) { + return true; + } + if (container->scratchpad) { + return true; + } + return false; +} + +// scene rect wants premultiplied colors +static void scene_rect_set_color(struct wlr_scene_rect *rect, + const float color[4], float opacity) { + const float premultiplied[] = { + color[0] * color[3] * opacity, + color[1] * color[3] * opacity, + color[2] * color[3] * opacity, + color[3] * opacity, + }; + + wlr_scene_rect_set_color(rect, premultiplied); +} + +void container_update(struct sway_container *con) { + struct border_colors *colors = container_get_current_colors(con); + list_t *siblings = NULL; + enum sway_container_layout layout = L_NONE; + float alpha = con->alpha; + + if (con->current.parent) { + siblings = con->current.parent->current.children; + layout = con->current.parent->current.layout; + } else if (con->current.workspace) { + siblings = con->current.workspace->current.tiling; + layout = con->current.workspace->current.layout; + } + + float bottom[4], right[4]; + memcpy(bottom, colors->child_border, sizeof(bottom)); + memcpy(right, colors->child_border, sizeof(right)); + + if (!container_is_current_floating(con) && siblings && siblings->length == 1) { + if (layout == L_HORIZ) { + memcpy(right, colors->indicator, sizeof(right)); + } else if (layout == L_VERT) { + memcpy(bottom, colors->indicator, sizeof(bottom)); + } + } + + struct wlr_scene_node *node; + wl_list_for_each(node, &con->title_bar.border->children, link) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + scene_rect_set_color(rect, colors->border, alpha); + } + + wl_list_for_each(node, &con->title_bar.background->children, link) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + scene_rect_set_color(rect, colors->background, alpha); + } + + if (con->view) { + scene_rect_set_color(con->border.top, colors->child_border, alpha); + scene_rect_set_color(con->border.bottom, bottom, alpha); + scene_rect_set_color(con->border.left, colors->child_border, alpha); + scene_rect_set_color(con->border.right, right, alpha); + } + + if (con->title_bar.title_text) { + sway_text_node_set_color(con->title_bar.title_text, colors->text); + sway_text_node_set_background(con->title_bar.title_text, colors->background); + } + + if (con->title_bar.marks_text) { + sway_text_node_set_color(con->title_bar.marks_text, colors->text); + sway_text_node_set_background(con->title_bar.marks_text, colors->background); + } +} + +void container_update_itself_and_parents(struct sway_container *con) { + container_update(con); + + if (con->current.parent) { + container_update_itself_and_parents(con->current.parent); + } +} + +static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) { + int len; + const pixman_box32_t *rects = pixman_region32_rectangles(region, &len); + + wlr_scene_node_set_enabled(&tree->node, len > 0); + if (len == 0) { + return; + } + + int i = 0; + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + wlr_scene_node_set_enabled(&rect->node, i < len); + + if (i < len) { + const pixman_box32_t *box = &rects[i++]; + wlr_scene_node_set_position(&rect->node, box->x1, box->y1); + wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1); + } + } +} + +void container_arrange_title_bar(struct sway_container *con) { + enum alignment title_align = config->title_align; + int marks_buffer_width = 0; + int width = con->title_width; + int height = container_titlebar_height(); + + pixman_region32_t text_area; + pixman_region32_init(&text_area); + + if (con->title_bar.marks_text) { + struct sway_text_node *node = con->title_bar.marks_text; + marks_buffer_width = node->width; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = config->titlebar_h_padding; + } else { + h_padding = width - config->titlebar_h_padding - marks_buffer_width; + } + + h_padding = MAX(h_padding, 0); + + int alloc_width = MIN((int)node->width, + width - h_padding - config->titlebar_h_padding); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + + pixman_region32_union_rect(&text_area, &text_area, + node->node->x, node->node->y, alloc_width, node->height); + } + + if (con->title_bar.title_text) { + struct sway_text_node *node = con->title_bar.title_text; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = width - config->titlebar_h_padding - node->width; + } else if (title_align == ALIGN_CENTER) { + h_padding = ((int)width - marks_buffer_width - node->width) >> 1; + } else { + h_padding = config->titlebar_h_padding; + } + + h_padding = MAX(h_padding, 0); + + int alloc_width = MIN((int) node->width, + width - h_padding - config->titlebar_h_padding); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + + pixman_region32_union_rect(&text_area, &text_area, + node->node->x, node->node->y, alloc_width, node->height); + } + + // silence pixman errors + if (width <= 0 || height <= 0) { + pixman_region32_fini(&text_area); + return; + } + + pixman_region32_t background, border; + + int thickness = config->titlebar_border_thickness; + pixman_region32_init_rect(&background, + thickness, thickness, + width - thickness * 2, height - thickness * 2); + pixman_region32_init_rect(&border, 0, 0, width, height); + pixman_region32_subtract(&border, &border, &background); + + pixman_region32_subtract(&background, &background, &text_area); + pixman_region32_fini(&text_area); + + update_rect_list(con->title_bar.background, &background); + pixman_region32_fini(&background); + + update_rect_list(con->title_bar.border, &border); + pixman_region32_fini(&border); + + container_update(con); +} + +void container_update_marks(struct sway_container *con) { + char *buffer = NULL; + + if (config->show_marks && con->marks->length) { + size_t len = 0; + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + len += strlen(mark) + 2; + } + } + buffer = calloc(len + 1, 1); + char *part = malloc(len + 1); + + if (!sway_assert(buffer && part, "Unable to allocate memory")) { + free(buffer); + return; + } + + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + snprintf(part, len + 1, "[%s]", mark); + strcat(buffer, part); + } + } + free(part); + } + + if (!buffer) { + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + } else if (!con->title_bar.marks_text) { + struct border_colors *colors = container_get_current_colors(con); + + con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, + buffer, colors->text, false); + } else { + sway_text_node_set_text(con->title_bar.marks_text, buffer); + } + + container_arrange_title_bar(con); + free(buffer); +} + +void container_update_title_bar(struct sway_container *con) { + if (!con->formatted_title) { + return; + } + + struct border_colors *colors = container_get_current_colors(con); + + if (con->title_bar.title_text) { + wlr_scene_node_destroy(con->title_bar.title_text->node); + con->title_bar.title_text = NULL; + } + + con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, + con->formatted_title, colors->text, config->pango_markup); + + // we always have to remake these text buffers completely for text font + // changes etc... + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + + container_update_marks(con); + container_arrange_title_bar(con); +} + void container_destroy(struct sway_container *con) { if (!sway_assert(con->node.destroying, "Tried to free container which wasn't marked as destroying")) { @@ -134,21 +451,11 @@ void container_destroy(struct sway_container *con) { } free(con->title); free(con->formatted_title); - wlr_texture_destroy(con->title_focused); - wlr_texture_destroy(con->title_focused_inactive); - wlr_texture_destroy(con->title_unfocused); - wlr_texture_destroy(con->title_urgent); - wlr_texture_destroy(con->title_focused_tab_title); list_free(con->pending.children); list_free(con->current.children); list_free(con->outputs); list_free_items_and_destroy(con->marks); - wlr_texture_destroy(con->marks_focused); - wlr_texture_destroy(con->marks_focused_inactive); - wlr_texture_destroy(con->marks_unfocused); - wlr_texture_destroy(con->marks_urgent); - wlr_texture_destroy(con->marks_focused_tab_title); if (con->view && con->view->container == con) { con->view->container = NULL; @@ -308,108 +615,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con) { return con->outputs->items[con->outputs->length - 1]; } -static void render_titlebar_text_texture(struct sway_output *output, - struct sway_container *con, struct wlr_texture **texture, - struct border_colors *class, bool pango_markup, char *text) { - double scale = output->wlr_output->scale; - int width = 0; - int height = config->font_height * scale; - int baseline; - - // We must use a non-nil cairo_t for cairo_set_font_options to work. - // Therefore, we cannot use cairo_create(NULL). - cairo_surface_t *dummy_surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, 0, 0); - cairo_t *c = cairo_create(dummy_surface); - cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); - cairo_font_options_t *fo = cairo_font_options_create(); - if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); - } else { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_subpixel_order(fo, - to_cairo_subpixel_order(output->wlr_output->subpixel)); - } - cairo_set_font_options(c, fo); - get_text_size(c, config->font_description, &width, NULL, &baseline, scale, - config->pango_markup, "%s", text); - cairo_surface_destroy(dummy_surface); - cairo_destroy(c); - - if (width == 0 || height == 0) { - return; - } - - if (height > config->font_height * scale) { - height = config->font_height * scale; - } - - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, width, height); - cairo_status_t status = cairo_surface_status(surface); - if (status != CAIRO_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", - cairo_status_to_string(status)); - return; - } - - cairo_t *cairo = cairo_create(surface); - cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); - cairo_set_font_options(cairo, fo); - cairo_font_options_destroy(fo); - cairo_set_source_rgba(cairo, class->background[0], class->background[1], - class->background[2], class->background[3]); - cairo_paint(cairo); - PangoContext *pango = pango_cairo_create_context(cairo); - cairo_set_source_rgba(cairo, class->text[0], class->text[1], - class->text[2], class->text[3]); - cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - - render_text(cairo, config->font_description, scale, pango_markup, "%s", text); - - cairo_surface_flush(surface); - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_image_surface_get_stride(surface); - struct wlr_renderer *renderer = output->wlr_output->renderer; - *texture = wlr_texture_from_pixels( - renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} - -static void update_title_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->formatted_title) { - return; - } - - render_titlebar_text_texture(output, con, texture, class, - config->pango_markup, con->formatted_title); -} - -void container_update_title_textures(struct sway_container *container) { - update_title_texture(container, &container->title_focused, - &config->border_colors.focused); - update_title_texture(container, &container->title_focused_inactive, - &config->border_colors.focused_inactive); - update_title_texture(container, &container->title_unfocused, - &config->border_colors.unfocused); - update_title_texture(container, &container->title_urgent, - &config->border_colors.urgent); - update_title_texture(container, &container->title_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(container); -} - /** * Calculate and return the length of the tree representation. * An example tree representation is: V[Terminal, Firefox] @@ -475,7 +680,13 @@ void container_update_representation(struct sway_container *con) { } container_build_representation(con->pending.layout, con->pending.children, con->formatted_title); - container_update_title_textures(con); + + if (con->title_bar.title_text) { + sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); + container_arrange_title_bar(con); + } else { + container_update_title_bar(con); + } } if (con->pending.parent) { container_update_representation(con->pending.parent); @@ -761,17 +972,6 @@ bool container_is_floating(struct sway_container *container) { return false; } -bool container_is_current_floating(struct sway_container *container) { - if (!container->current.parent && container->current.workspace && - list_find(container->current.workspace->floating, container) != -1) { - return true; - } - if (container->scratchpad) { - return true; - } - return false; -} - void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->pending.x; box->y = container->pending.y; @@ -1092,7 +1292,6 @@ void container_discover_outputs(struct sway_container *con) { .width = con->current.width, .height = con->current.height, }; - struct sway_output *old_output = container_get_effective_output(con); for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; @@ -1129,14 +1328,6 @@ void container_discover_outputs(struct sway_container *con) { list_del(con->outputs, index); } } - struct sway_output *new_output = container_get_effective_output(con); - double old_scale = old_output && old_output->enabled ? - old_output->wlr_output->scale : -1; - double new_scale = new_output ? new_output->wlr_output->scale : -1; - if (old_scale != new_scale) { - container_update_title_textures(con); - container_update_marks_textures(con); - } } enum sway_container_layout container_parent_layout(struct sway_container *con) { @@ -1149,14 +1340,6 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { return L_NONE; } -enum sway_container_layout container_current_parent_layout( - struct sway_container *con) { - if (con->current.parent) { - return con->current.parent->current.layout; - } - return con->current.workspace->current.layout; -} - list_t *container_get_siblings(struct sway_container *container) { if (container->pending.parent) { return container->pending.parent->pending.children; @@ -1174,13 +1357,6 @@ int container_sibling_index(struct sway_container *child) { return list_find(container_get_siblings(child), child); } -list_t *container_get_current_siblings(struct sway_container *container) { - if (container->current.parent) { - return container->current.parent->current.children; - } - return container->current.workspace->current.tiling; -} - void container_handle_fullscreen_reparent(struct sway_container *con) { if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || con->pending.workspace->fullscreen == con) { @@ -1395,7 +1571,7 @@ bool container_find_and_unmark(char *mark) { if (strcmp(con_mark, mark) == 0) { free(con_mark); list_del(con->marks, i); - container_update_marks_textures(con); + container_update_marks(con); ipc_event_window(con, "mark"); return true; } @@ -1426,70 +1602,15 @@ void container_add_mark(struct sway_container *con, char *mark) { ipc_event_window(con, "mark"); } -static void update_marks_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->marks->length) { - return; - } - - size_t len = 0; - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - len += strlen(mark) + 2; - } - } - char *buffer = calloc(len + 1, 1); - char *part = malloc(len + 1); - - if (!sway_assert(buffer && part, "Unable to allocate memory")) { - free(buffer); - return; - } - - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - snprintf(part, len + 1, "[%s]", mark); - strcat(buffer, part); - } - } - free(part); - - render_titlebar_text_texture(output, con, texture, class, false, buffer); - - free(buffer); -} - -void container_update_marks_textures(struct sway_container *con) { - if (!config->show_marks) { - return; - } - update_marks_texture(con, &con->marks_focused, - &config->border_colors.focused); - update_marks_texture(con, &con->marks_focused_inactive, - &config->border_colors.focused_inactive); - update_marks_texture(con, &con->marks_unfocused, - &config->border_colors.unfocused); - update_marks_texture(con, &con->marks_urgent, - &config->border_colors.urgent); - update_marks_texture(con, &con->marks_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(con); -} - void container_raise_floating(struct sway_container *con) { // Bring container to front by putting it at the end of the floating list. struct sway_container *floater = container_toplevel_ancestor(con); if (container_is_floating(floater) && floater->pending.workspace) { + // it's okay to just raise the scene directly instead of waiting + // for the transaction to go through. We won't be reconfiguring + // surfaces + wlr_scene_node_raise_to_top(&floater->scene_tree->node); + list_move_to_end(floater->pending.workspace->floating, floater); node_set_dirty(&floater->pending.workspace->node); } diff --git a/sway/tree/view.c b/sway/tree/view.c index d349e5faf..7af2fd3f7 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -27,6 +27,7 @@ #include "sway/scene_descriptor.h" #include "sway/server.h" #include "sway/surface.h" +#include "sway/sway_text_node.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -1337,7 +1338,13 @@ void view_update_title(struct sway_view *view, bool force) { view->container->title = title ? strdup(title) : NULL; // Update title after the global font height is updated - container_update_title_textures(view->container); + if (view->container->title_bar.title_text && len) { + sway_text_node_set_text(view->container->title_bar.title_text, + view->container->formatted_title); + container_arrange_title_bar(view->container); + } else { + container_update_title_bar(view->container); + } ipc_event_window(view->container, "title"); @@ -1404,6 +1411,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { return; } clock_gettime(CLOCK_MONOTONIC, &view->urgent); + container_update_itself_and_parents(view->container); } else { view->urgent = (struct timespec){ 0 }; if (view->urgent_timer) { From 08c484f46f130aa7e590ef4bcb39d2ceed7160f6 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 4 Mar 2022 20:38:04 -0500 Subject: [PATCH 19/36] transaction: ready signals will return success bools --- include/sway/desktop/transaction.h | 11 +++++++++-- sway/desktop/transaction.c | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7dd58ba8c..17d41fa35 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -1,6 +1,7 @@ #ifndef _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H #include +#include /** * Transactions enable us to perform atomic layout updates. @@ -38,8 +39,11 @@ void transaction_commit_dirty_client(void); * Notify the transaction system that a view is ready for the new layout. * * When all views in the transaction are ready, the layout will be applied. + * + * A success boolean is returned denoting that this part of the transaction is + * ready. */ -void transaction_notify_view_ready_by_serial(struct sway_view *view, +bool transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial); /** @@ -47,8 +51,11 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view, * identifying the instruction by geometry rather than by serial. * * This is used by xwayland views, as they don't have serials. + * + * A success boolean is returned denoting that this part of the transaction is + * ready. */ -void transaction_notify_view_ready_by_geometry(struct sway_view *view, +bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height); #endif diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 6947e1384..bb725795b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -499,16 +499,18 @@ static void set_instruction_ready( transaction_progress(); } -void transaction_notify_view_ready_by_serial(struct sway_view *view, +bool transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial) { struct sway_transaction_instruction *instruction = view->container->node.instruction; if (instruction != NULL && instruction->serial == serial) { set_instruction_ready(instruction); + return true; } + return false; } -void transaction_notify_view_ready_by_geometry(struct sway_view *view, +bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height) { struct sway_transaction_instruction *instruction = view->container->node.instruction; @@ -518,7 +520,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, instruction->container_state.content_width == width && instruction->container_state.content_height == height) { set_instruction_ready(instruction); + return true; } + return false; } static void _transaction_commit_dirty(bool server_request) { From b38ed8b4792928dca3e1580e8160792ea41e25c4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 6 Dec 2023 14:28:59 -0500 Subject: [PATCH 20/36] scene_graph: Port xdg_shell --- include/sway/scene_descriptor.h | 1 + include/sway/tree/view.h | 46 +----- sway/desktop/output.c | 8 +- sway/desktop/xdg_shell.c | 113 ++++++------- sway/input/cursor.c | 13 +- sway/tree/view.c | 272 -------------------------------- 6 files changed, 81 insertions(+), 372 deletions(-) diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 970adaa5b..43991f771 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -16,6 +16,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_CONTAINER, SWAY_SCENE_DESC_VIEW, SWAY_SCENE_DESC_LAYER_SHELL, + SWAY_SCENE_DESC_POPUP, SWAY_SCENE_DESC_DRAG_ICON, }; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 4aaed9e3e..467d912f9 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -123,8 +123,6 @@ struct sway_view { struct wl_signal unmap; } events; - struct wl_listener surface_new_subsurface; - int max_render_time; // In milliseconds enum seat_config_shortcuts_inhibit shortcuts_inhibit; @@ -191,43 +189,12 @@ struct sway_xwayland_unmanaged { struct wl_listener override_redirect; }; #endif -struct sway_view_child; - -struct sway_view_child_impl { - void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy); - void (*destroy)(struct sway_view_child *child); -}; - -/** - * A view child is a surface in the view tree, such as a subsurface or a popup. - */ -struct sway_view_child { - const struct sway_view_child_impl *impl; - struct wl_list link; - - struct sway_view *view; - struct sway_view_child *parent; - struct wl_list children; // sway_view_child::link - struct wlr_surface *surface; - bool mapped; - - struct wl_listener surface_commit; - struct wl_listener surface_new_subsurface; - struct wl_listener surface_map; - struct wl_listener surface_unmap; - struct wl_listener surface_destroy; - struct wl_listener view_unmap; -}; - -struct sway_subsurface { - struct sway_view_child child; - - struct wl_listener destroy; -}; struct sway_xdg_popup { - struct sway_view_child child; + struct sway_view *view; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *xdg_surface_tree; struct wlr_xdg_popup *wlr_xdg_popup; struct wl_listener surface_commit; @@ -339,13 +306,6 @@ void view_unmap(struct sway_view *view); void view_update_size(struct sway_view *view); void view_center_surface(struct sway_view *view); -void view_child_init(struct sway_view_child *child, - const struct sway_view_child_impl *impl, struct sway_view *view, - struct wlr_surface *surface); - -void view_child_destroy(struct sway_view_child *child); - - struct sway_view *view_from_wlr_xdg_surface( struct wlr_xdg_surface *xdg_surface); #if HAVE_XWAYLAND diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 942bc780b..a51844847 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -289,8 +289,14 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, } struct wlr_scene_node *current = &buffer->node; - while (true) { + struct sway_view *view = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_VIEW); + if (view) { + view_max_render_time = view->max_render_time; + break; + } + if (!current->parent) { break; } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 11c112bee..fed820cf6 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -7,7 +7,7 @@ #include #include "log.h" #include "sway/decoration.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -19,41 +19,29 @@ #include "sway/tree/workspace.h" #include "sway/xdg_decoration.h" -static const struct sway_view_child_impl popup_impl; +static struct sway_xdg_popup *popup_create( + struct wlr_xdg_popup *wlr_popup, struct sway_view *view, + struct wlr_scene_tree *parent); -static void popup_get_view_coords(struct sway_view_child *child, - int *sx, int *sy) { - struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; - - wlr_xdg_popup_get_toplevel_coords(wlr_popup, - wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, - wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, - sx, sy); +static void popup_handle_new_popup(struct wl_listener *listener, void *data) { + struct sway_xdg_popup *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); } -static void popup_destroy(struct sway_view_child *child) { - if (!sway_assert(child->impl == &popup_impl, - "Expected an xdg_shell popup")) { - return; - } - struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - wl_list_remove(&popup->surface_commit.link); +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->surface_commit.link); + wlr_scene_node_destroy(&popup->scene_tree->node); free(popup); } -static const struct sway_view_child_impl popup_impl = { - .get_view_coords = popup_get_view_coords, - .destroy = popup_destroy, -}; - -static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view); - static void popup_unconstrain(struct sway_xdg_popup *popup) { - struct sway_view *view = popup->child.view; + struct sway_view *view = popup->view; struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; struct sway_workspace *workspace = view->container->pending.workspace; @@ -83,29 +71,44 @@ static void popup_handle_surface_commit(struct wl_listener *listener, void *data } } -static void popup_handle_new_popup(struct wl_listener *listener, void *data) { - struct sway_xdg_popup *popup = - wl_container_of(listener, popup, new_popup); - struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, popup->child.view); -} - -static void popup_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); - view_child_destroy(&popup->child); -} - -static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { +static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, + struct sway_view *view, struct wlr_scene_tree *parent) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; - struct sway_xdg_popup *popup = - calloc(1, sizeof(struct sway_xdg_popup)); - if (popup == NULL) { + struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); + if (!popup) { return NULL; } - view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); + popup->wlr_xdg_popup = wlr_popup; + popup->view = view; + + popup->scene_tree = wlr_scene_tree_create(parent); + if (!popup->scene_tree) { + free(popup); + return NULL; + } + + popup->xdg_surface_tree = wlr_scene_xdg_surface_create( + popup->scene_tree, xdg_surface); + if (!popup->xdg_surface_tree) { + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return NULL; + } + + if (!scene_descriptor_assign(&popup->scene_tree->node, + SWAY_SCENE_DESC_POPUP, popup)) { + sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return NULL; + } + + popup->wlr_xdg_popup = xdg_surface->popup; + struct sway_xdg_shell_view *shell_view = + wl_container_of(view, shell_view, view); + xdg_surface->data = shell_view; wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); popup->surface_commit.notify = popup_handle_surface_commit; @@ -114,9 +117,6 @@ static struct sway_xdg_popup *popup_create( wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&xdg_surface->surface->events.map, &popup->child.surface_map); - wl_signal_add(&xdg_surface->surface->events.unmap, &popup->child.surface_unmap); - return popup; } @@ -317,7 +317,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - desktop_damage_view(view); memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); @@ -330,15 +329,12 @@ static void handle_commit(struct wl_listener *listener, void *data) { } else { view_center_surface(view); } - desktop_damage_view(view); } if (view->container->node.instruction) { transaction_notify_view_ready_by_serial(view, xdg_surface->current.configure_serial); } - - view_damage_from(view); } static void handle_set_title(struct wl_listener *listener, void *data) { @@ -360,7 +356,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, &xdg_shell_view->view); + + struct sway_xdg_popup *popup = popup_create(wlr_popup, + &xdg_shell_view->view, root->layers.popup); + if (!popup) { + return; + } + + int lx, ly; + wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); } static void handle_request_maximize(struct wl_listener *listener, void *data) { @@ -567,5 +572,7 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { xdg_shell_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); + wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); + xdg_toplevel->base->data = xdg_shell_view; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 30df76f45..fd8f50d45 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -80,6 +80,7 @@ struct sway_node *node_at_coords( while (true) { struct sway_container *con = scene_descriptor_try_get(current, SWAY_SCENE_DESC_CONTAINER); + if (!con) { struct sway_view *view = scene_descriptor_try_get(current, SWAY_SCENE_DESC_VIEW); @@ -88,12 +89,18 @@ struct sway_node *node_at_coords( } } - if (con) { - if (!con->view || con->view->surface) { - return &con->node; + if (!con) { + struct sway_xdg_popup *popup = + scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); + if (popup) { + con = popup->view->container; } } + if (con && (!con->view || con->view->surface)) { + return &con->node; + } + if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { // We don't want to feed through the current workspace on // layer shells diff --git a/sway/tree/view.c b/sway/tree/view.c index 7af2fd3f7..ee25faf1e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -488,24 +488,6 @@ void view_for_each_popup_surface(struct sway_view *view, view->impl->for_each_popup_surface(view, iterator, user_data); } } - -static void view_subsurface_create(struct sway_view *view, - struct wlr_subsurface *subsurface); - -static void view_init_subsurfaces(struct sway_view *view, - struct wlr_surface *surface); - -static void view_child_init_subsurfaces(struct sway_view_child *view_child, - struct wlr_surface *surface); - -static void view_handle_surface_new_subsurface(struct wl_listener *listener, - void *data) { - struct sway_view *view = - wl_container_of(listener, view, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - view_subsurface_create(view, subsurface); -} - static bool view_has_executed_criteria(struct sway_view *view, struct criteria *criteria) { for (int i = 0; i < view->executed_criteria->length; ++i) { @@ -826,11 +808,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } ipc_event_window(view->container, "new"); - view_init_subsurfaces(view, wlr_surface); - wl_signal_add(&wlr_surface->events.new_subsurface, - &view->surface_new_subsurface); - view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; - if (decoration) { view_update_csd_from_client(view, decoration); } @@ -897,8 +874,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, void view_unmap(struct sway_view *view) { wl_signal_emit_mutable(&view->events.unmap, view); - wl_list_remove(&view->surface_new_subsurface.link); - view->executed_criteria->length = 0; if (view->urgent_timer) { @@ -962,253 +937,6 @@ void view_center_surface(struct sway_view *view) { (con->current.content_height - view->geometry.height) / 2); } -static const struct sway_view_child_impl subsurface_impl; - -static void subsurface_get_view_coords(struct sway_view_child *child, - int *sx, int *sy) { - struct wlr_surface *surface = child->surface; - if (child->parent && child->parent->impl && - child->parent->impl->get_view_coords) { - child->parent->impl->get_view_coords(child->parent, sx, sy); - } else { - *sx = *sy = 0; - } - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(surface); - *sx += subsurface->current.x; - *sy += subsurface->current.y; -} - -static void subsurface_destroy(struct sway_view_child *child) { - if (!sway_assert(child->impl == &subsurface_impl, - "Expected a subsurface")) { - return; - } - struct sway_subsurface *subsurface = (struct sway_subsurface *)child; - wl_list_remove(&subsurface->destroy.link); - free(subsurface); -} - -static const struct sway_view_child_impl subsurface_impl = { - .get_view_coords = subsurface_get_view_coords, - .destroy = subsurface_destroy, -}; - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - struct sway_view_child *child = &subsurface->child; - view_child_destroy(child); -} - -static void view_child_damage(struct sway_view_child *child, bool whole); - -static void view_subsurface_create(struct sway_view *view, - struct wlr_subsurface *wlr_subsurface) { - struct sway_subsurface *subsurface = - calloc(1, sizeof(struct sway_subsurface)); - if (subsurface == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - view_child_init(&subsurface->child, &subsurface_impl, view, - wlr_subsurface->surface); - - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->destroy.notify = subsurface_handle_destroy; - - subsurface->child.mapped = true; - - view_child_damage(&subsurface->child, true); -} - -static void view_child_subsurface_create(struct sway_view_child *child, - struct wlr_subsurface *wlr_subsurface) { - struct sway_subsurface *subsurface = - calloc(1, sizeof(struct sway_subsurface)); - if (subsurface == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - subsurface->child.parent = child; - wl_list_insert(&child->children, &subsurface->child.link); - view_child_init(&subsurface->child, &subsurface_impl, child->view, - wlr_subsurface->surface); - - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->destroy.notify = subsurface_handle_destroy; - - subsurface->child.mapped = true; - - view_child_damage(&subsurface->child, true); -} - -static bool view_child_is_mapped(struct sway_view_child *child) { - while (child) { - if (!child->mapped) { - return false; - } - child = child->parent; - } - return true; -} - -static void view_child_damage(struct sway_view_child *child, bool whole) { - if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { - return; - } - int sx, sy; - child->impl->get_view_coords(child, &sx, &sy); - desktop_damage_surface(child->surface, - child->view->container->pending.content_x - - child->view->geometry.x + sx, - child->view->container->pending.content_y - - child->view->geometry.y + sy, whole); -} - -static void view_child_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_commit); - view_child_damage(child, false); -} - -static void view_child_handle_surface_new_subsurface( - struct wl_listener *listener, void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - view_child_subsurface_create(child, subsurface); -} - -static void view_child_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_destroy); - view_child_destroy(child); -} - -static void view_init_subsurfaces(struct sway_view *view, - struct wlr_surface *surface) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - view_subsurface_create(view, subsurface); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - view_subsurface_create(view, subsurface); - } -} - -static void view_child_init_subsurfaces(struct sway_view_child *view_child, - struct wlr_surface *surface) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - view_child_subsurface_create(view_child, subsurface); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - view_child_subsurface_create(view_child, subsurface); - } -} - -static void view_child_handle_surface_map(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_map); - child->mapped = true; - view_child_damage(child, true); -} - -static void view_child_handle_surface_unmap(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_unmap); - view_child_damage(child, true); - child->mapped = false; -} - -static void view_child_handle_view_unmap(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, view_unmap); - view_child_damage(child, true); - child->mapped = false; -} - -void view_child_init(struct sway_view_child *child, - const struct sway_view_child_impl *impl, struct sway_view *view, - struct wlr_surface *surface) { - child->impl = impl; - child->view = view; - child->surface = surface; - wl_list_init(&child->children); - - wl_signal_add(&surface->events.commit, &child->surface_commit); - child->surface_commit.notify = view_child_handle_surface_commit; - wl_signal_add(&surface->events.new_subsurface, - &child->surface_new_subsurface); - child->surface_new_subsurface.notify = - view_child_handle_surface_new_subsurface; - wl_signal_add(&surface->events.destroy, &child->surface_destroy); - child->surface_destroy.notify = view_child_handle_surface_destroy; - - // Not all child views have a map/unmap event - child->surface_map.notify = view_child_handle_surface_map; - wl_list_init(&child->surface_map.link); - child->surface_unmap.notify = view_child_handle_surface_unmap; - wl_list_init(&child->surface_unmap.link); - - wl_signal_add(&view->events.unmap, &child->view_unmap); - child->view_unmap.notify = view_child_handle_view_unmap; - - struct sway_container *container = child->view->container; - if (container != NULL) { - struct sway_workspace *workspace = container->pending.workspace; - if (workspace) { - surface_enter_output(child->surface, workspace->output); - } - } - - view_child_init_subsurfaces(child, surface); -} - -void view_child_destroy(struct sway_view_child *child) { - if (view_child_is_mapped(child) && child->view->container != NULL) { - view_child_damage(child, true); - } - - if (child->parent != NULL) { - wl_list_remove(&child->link); - child->parent = NULL; - } - - struct sway_view_child *subchild, *tmpchild; - wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { - wl_list_remove(&subchild->link); - subchild->parent = NULL; - // The subchild lost its parent link, so it cannot see that the parent - // is unmapped. Unmap it directly. - subchild->mapped = false; - } - - wl_list_remove(&child->surface_commit.link); - wl_list_remove(&child->surface_destroy.link); - wl_list_remove(&child->surface_map.link); - wl_list_remove(&child->surface_unmap.link); - wl_list_remove(&child->view_unmap.link); - wl_list_remove(&child->surface_new_subsurface.link); - - if (child->impl && child->impl->destroy) { - child->impl->destroy(child); - } else { - free(child); - } -} - struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *xdg_surface; if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { From 6e5fc4c2aafd211323c6037aa868c075852bfe15 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 18 Jan 2024 10:02:41 -0500 Subject: [PATCH 21/36] scene_graph: Port xwayland --- include/sway/output.h | 6 --- include/sway/scene_descriptor.h | 1 + include/sway/tree/root.h | 7 ++- include/sway/tree/view.h | 8 ++-- sway/desktop/output.c | 17 ------- sway/desktop/xwayland.c | 81 +++++++++++++++++---------------- sway/input/cursor.c | 6 +++ sway/tree/root.c | 6 +-- 8 files changed, 61 insertions(+), 71 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index ea5d8f47e..e20233061 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -141,12 +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); -#if HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct sway_output *output, - struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, - void *user_data); -#endif - void output_for_each_workspace(struct sway_output *output, void (*f)(struct sway_workspace *ws, void *data), void *data); diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 43991f771..2649d7c29 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -16,6 +16,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_CONTAINER, SWAY_SCENE_DESC_VIEW, SWAY_SCENE_DESC_LAYER_SHELL, + SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, SWAY_SCENE_DESC_POPUP, SWAY_SCENE_DESC_DRAG_ICON, }; diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 2f717baea..15df0f551 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -47,16 +47,15 @@ struct sway_root { struct wlr_scene_tree *shell_top; struct wlr_scene_tree *fullscreen; struct wlr_scene_tree *fullscreen_global; +#if HAVE_XWAYLAND + struct wlr_scene_tree *unmanaged; +#endif struct wlr_scene_tree *shell_overlay; struct wlr_scene_tree *popup; struct wlr_scene_tree *seat; struct wlr_scene_tree *session_lock; } layers; -#if HAVE_XWAYLAND - struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link -#endif - // Includes disabled outputs struct wl_list all_outputs; // sway_output::link diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 467d912f9..8493958e4 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -147,6 +147,8 @@ struct sway_xdg_shell_view { struct sway_xwayland_view { struct sway_view view; + struct wlr_scene_tree *surface_tree; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -168,18 +170,18 @@ struct sway_xwayland_view { struct wl_listener unmap; struct wl_listener destroy; struct wl_listener override_redirect; + + struct wl_listener surface_tree_destroy; }; struct sway_xwayland_unmanaged { struct wlr_xwayland_surface *wlr_xwayland_surface; - struct wl_list link; - int lx, ly; + struct wlr_scene_surface *surface_scene; struct wl_listener request_activate; struct wl_listener request_configure; struct wl_listener request_fullscreen; - struct wl_listener commit; struct wl_listener set_geometry; struct wl_listener associate; struct wl_listener dissociate; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a51844847..36c8f52ce 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -182,23 +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); } -#if HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct sway_output *output, - struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->lx; - double oy = unmanaged_surface->ly - output->ly; - - output_surface_for_each_surface(output, xsurface->surface, ox, oy, - iterator, user_data); - } -} -#endif - static int scale_length(int length, int offset, float scale) { return roundf((offset + length) * scale) - roundf(offset * scale); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 8f79b5e79..183bdba2c 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -6,15 +6,16 @@ #include #include #include +#include #include #include #include "log.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/server.h" @@ -45,29 +46,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, ev->width, ev->height); } -static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, commit); - struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - false); -} - static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, set_geometry); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - if (xsurface->x != surface->lx || xsurface->y != surface->ly) { - // Surface has moved - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - true); - surface->lx = xsurface->x; - surface->ly = xsurface->y; - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - true); - } + wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); } static void unmanaged_handle_map(struct wl_listener *listener, void *data) { @@ -75,17 +59,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { wl_container_of(listener, surface, map); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); + surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, + xsurface->surface); - wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); - surface->set_geometry.notify = unmanaged_handle_set_geometry; + if (surface->surface_scene) { + scene_descriptor_assign(&surface->surface_scene->buffer->node, + SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); + wlr_scene_node_set_position(&surface->surface_scene->buffer->node, + xsurface->x, xsurface->y); - wl_signal_add(&xsurface->surface->events.commit, &surface->commit); - surface->commit.notify = unmanaged_handle_commit; - - surface->lx = xsurface->x; - surface->ly = xsurface->y; - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); + wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); + surface->set_geometry.notify = unmanaged_handle_set_geometry; + } if (wlr_xwayland_or_surface_wants_focus(xsurface)) { struct sway_seat *seat = input_manager_current_seat(); @@ -99,10 +84,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, unmap); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); - wl_list_remove(&surface->link); - wl_list_remove(&surface->set_geometry.link); - wl_list_remove(&surface->commit.link); + + if (surface->surface_scene) { + wl_list_remove(&surface->set_geometry.link); + + wlr_scene_node_destroy(&surface->surface_scene->buffer->node); + surface->surface_scene = NULL; + } struct sway_seat *seat = input_manager_current_seat(); if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { @@ -455,7 +443,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - desktop_damage_view(view); memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); @@ -463,15 +450,12 @@ static void handle_commit(struct wl_listener *listener, void *data) { } else { view_center_surface(view); } - desktop_damage_view(view); } if (view->container->node.instruction) { transaction_notify_view_ready_by_geometry(view, xsurface->x, xsurface->y, state->width, state->height); } - - view_damage_from(view); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -515,9 +499,21 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - view_unmap(view); - wl_list_remove(&xwayland_view->commit.link); + wl_list_remove(&xwayland_view->surface_tree_destroy.link); + + if (xwayland_view->surface_tree) { + wlr_scene_node_destroy(&xwayland_view->surface_tree->node); + xwayland_view->surface_tree = NULL; + } + + view_unmap(view); +} + +static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, + surface_tree_destroy); + xwayland_view->surface_tree = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -537,6 +533,15 @@ static void handle_map(struct wl_listener *listener, void *data) { // Put it back into the tree view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); + xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( + xwayland_view->view.content_tree, xsurface->surface); + + if (xwayland_view->surface_tree) { + xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy; + wl_signal_add(&xwayland_view->surface_tree->node.events.destroy, + &xwayland_view->surface_tree_destroy); + } + transaction_commit_dirty(); } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fd8f50d45..404c1eed5 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -107,6 +107,12 @@ struct sway_node *node_at_coords( return NULL; } +#if HAVE_XWAYLAND + if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { + return NULL; + } +#endif + if (!current->parent) { break; } diff --git a/sway/tree/root.c b/sway/tree/root.c index 7c8f9ea69..e9cea5e22 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -54,6 +54,9 @@ struct sway_root *root_create(struct wl_display *wl_display) { 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); +#if HAVE_XWAYLAND + root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); +#endif 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); @@ -74,9 +77,6 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->output_layout = wlr_output_layout_create(wl_display); wl_list_init(&root->all_outputs); -#if HAVE_XWAYLAND - wl_list_init(&root->xwayland_unmanaged); -#endif wl_signal_init(&root->events.new_node); root->outputs = create_list(); root->non_desktop_outputs = create_list(); From ed2724bd6c83ad3fdc2010b3a1e2f5967f0e8b38 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 1 Mar 2022 00:48:22 -0500 Subject: [PATCH 22/36] xwayland: Cleanup geometry handling on commit Instead of doing this roundabout thing where we get the surface from the view, let's instead get it from the `wlr_surface_state` that we already track in `handle_commit`. This makes the NULL state impossible which is what the old `get_geometry` is checking for and generally cleans things up a little bit. Also don't check if the geometry x/y changed, those will always be 0 for xwayland. --- sway/desktop/xwayland.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 183bdba2c..0967c7fc2 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -414,17 +414,6 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; -static void get_geometry(struct sway_view *view, struct wlr_box *box) { - box->x = box->y = 0; - if (view->surface) { - box->width = view->surface->current.width; - box->height = view->surface->current.height; - } else { - box->width = 0; - box->height = 0; - } -} - static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit); @@ -432,12 +421,12 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; struct wlr_surface_state *state = &xsurface->surface->current; - struct wlr_box new_geo; - get_geometry(view, &new_geo); + struct wlr_box new_geo = {0}; + new_geo.width = state->width; + new_geo.height = state->height; + bool new_size = new_geo.width != view->geometry.width || - new_geo.height != view->geometry.height || - new_geo.x != view->geometry.x || - new_geo.y != view->geometry.y; + new_geo.height != view->geometry.height; if (new_size) { // The client changed its surface size in this commit. For floating From 06ad734e70227ad0527fe11b88ad37e93005ce0c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 27 Apr 2023 10:25:40 +0200 Subject: [PATCH 23/36] scene_graph: Port view saved buffers --- include/sway/tree/view.h | 18 ++------ sway/desktop/transaction.c | 49 ++++++---------------- sway/desktop/xdg_shell.c | 10 ++++- sway/desktop/xwayland.c | 10 ++++- sway/tree/view.c | 86 +++++++++++++++++++++++++------------- 5 files changed, 90 insertions(+), 83 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 8493958e4..66d6db1c4 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -57,21 +57,13 @@ struct sway_view_impl { void (*destroy)(struct sway_view *view); }; -struct sway_saved_buffer { - struct wlr_client_buffer *buffer; - int x, y; - int width, height; - enum wl_output_transform transform; - struct wlr_fbox source_box; - struct wl_list link; // sway_view::saved_buffers -}; - struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *content_tree; + struct wlr_scene_tree *saved_surface_tree; struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views @@ -92,16 +84,10 @@ struct sway_view { bool allow_request_urgent; struct wl_event_source *urgent_timer; - struct wl_list saved_buffers; // sway_saved_buffer::link - // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. struct wlr_box geometry; - // The "old" geometry during a transaction. Used to damage the old location - // when a transaction is applied. - struct wlr_box saved_geometry; - struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; struct wl_listener foreign_fullscreen_request; @@ -347,4 +333,6 @@ bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); +void view_send_frame_done(struct sway_view *view); + #endif diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index bb725795b..1ae6642bb 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -232,21 +232,6 @@ static void apply_workspace_state(struct sway_workspace *ws, static void apply_container_state(struct sway_container *container, struct sway_container_state *state) { struct sway_view *view = container->view; - // Damage the old location - desktop_damage_whole_container(container); - if (view && !wl_list_empty(&view->saved_buffers)) { - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - struct wlr_box box = { - .x = saved_buf->x - view->saved_geometry.x, - .y = saved_buf->y - view->saved_geometry.y, - .width = saved_buf->width, - .height = saved_buf->height, - }; - desktop_damage_box(&box); - } - } - // There are separate children lists for each instruction state, the // container's current state and the container's pending state // (ie. con->children). The list itself needs to be freed here. @@ -256,17 +241,19 @@ static void apply_container_state(struct sway_container *container, memcpy(&container->current, state, sizeof(struct sway_container_state)); - if (view && !wl_list_empty(&view->saved_buffers)) { - if (!container->node.destroying || container->node.ntxnrefs == 1) { - view_remove_saved_buffer(view); + if (view) { + if (view->saved_surface_tree) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { + view_remove_saved_buffer(view); + } } - } - // If the view hasn't responded to the configure, center it within - // the container. This is important for fullscreen views which - // refuse to resize to the size of the output. - if (view && view->surface) { - view_center_surface(view); + // If the view hasn't responded to the configure, center it within + // the container. This is important for fullscreen views which + // refuse to resize to the size of the output. + if (view->surface) { + view_center_surface(view); + } } // Damage the new location @@ -415,21 +402,11 @@ static void transaction_commit(struct sway_transaction *transaction) { ++transaction->num_waiting; } - // From here on we are rendering a saved buffer of the view, which - // means we can send a frame done event to make the client redraw it - // as soon as possible. Additionally, this is required if a view is - // mapping and its default geometry doesn't intersect an output. - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done( - node->sway_container->view->surface, &now); + view_send_frame_done(node->sway_container->view); } if (!hidden && node_is_view(node) && - wl_list_empty(&node->sway_container->view->saved_buffers)) { + !node->sway_container->view->saved_surface_tree) { view_save_buffer(node->sway_container->view); - memcpy(&node->sway_container->view->saved_geometry, - &node->sway_container->view->geometry, - sizeof(struct wlr_box)); } node->instruction = instruction; } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index fed820cf6..95b5cb9d7 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -332,8 +332,16 @@ static void handle_commit(struct wl_listener *listener, void *data) { } if (view->container->node.instruction) { - transaction_notify_view_ready_by_serial(view, + bool successful = transaction_notify_view_ready_by_serial(view, xdg_surface->current.configure_serial); + + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 0967c7fc2..e0c80c074 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -442,8 +442,16 @@ static void handle_commit(struct wl_listener *listener, void *data) { } if (view->container->node.instruction) { - transaction_notify_view_ready_by_geometry(view, + bool successful = transaction_notify_view_ready_by_geometry(view, xsurface->x, xsurface->y, state->width, state->height); + + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } } } diff --git a/sway/tree/view.c b/sway/tree/view.c index ee25faf1e..402fa179e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -56,7 +56,6 @@ bool view_init(struct sway_view *view, enum sway_view_type type, view->type = type; view->impl = impl; view->executed_criteria = create_list(); - wl_list_init(&view->saved_buffers); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; wl_signal_init(&view->events.unmap); @@ -77,9 +76,6 @@ void view_destroy(struct sway_view *view) { return; } wl_list_remove(&view->events.unmap.listener_list); - if (!wl_list_empty(&view->saved_buffers)) { - view_remove_saved_buffer(view); - } list_free(view->executed_criteria); view_assign_ctx(view, NULL); @@ -931,10 +927,10 @@ void view_center_surface(struct sway_view *view) { struct sway_container *con = view->container; // We always center the current coordinates rather than the next, as the // geometry immediately affects the currently active rendering. - con->surface_x = fmax(con->current.content_x, con->current.content_x + - (con->current.content_width - view->geometry.width) / 2); - con->surface_y = fmax(con->current.content_y, con->current.content_y + - (con->current.content_height - view->geometry.height) / 2); + int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); + int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); + + wlr_scene_node_set_position(&view->content_tree->node, x, y); } struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { @@ -1161,40 +1157,54 @@ bool view_is_urgent(struct sway_view *view) { } void view_remove_saved_buffer(struct sway_view *view) { - if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { + if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { return; } - struct sway_saved_buffer *saved_buf, *tmp; - wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { - wlr_buffer_unlock(&saved_buf->buffer->base); - wl_list_remove(&saved_buf->link); - free(saved_buf); - } + + wlr_scene_node_destroy(&view->saved_surface_tree->node); + view->saved_surface_tree = NULL; + wlr_scene_node_set_enabled(&view->content_tree->node, true); } -static void view_save_buffer_iterator(struct wlr_surface *surface, +static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *data) { - struct sway_view *view = data; + struct wlr_scene_tree *tree = data; - if (surface && surface->buffer) { - wlr_buffer_lock(&surface->buffer->base); - struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); - saved_buffer->buffer = surface->buffer; - saved_buffer->width = surface->current.width; - saved_buffer->height = surface->current.height; - saved_buffer->x = view->container->surface_x + sx; - saved_buffer->y = view->container->surface_y + sy; - saved_buffer->transform = surface->current.transform; - wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); - wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); + struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); + if (!sbuf) { + sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); + return; } + + wlr_scene_buffer_set_dest_size(sbuf, + buffer->dst_width, buffer->dst_height); + wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); + wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); + wlr_scene_node_set_position(&sbuf->node, sx, sy); + wlr_scene_buffer_set_transform(sbuf, buffer->transform); + wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); } void view_save_buffer(struct sway_view *view) { - if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { + if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { view_remove_saved_buffer(view); } - view_for_each_surface(view, view_save_buffer_iterator, view); + + view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); + if (!view->saved_surface_tree) { + sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); + return; + } + + // Enable and disable the saved surface tree like so to atomitaclly update + // the tree. This will prevent over damaging or other weirdness. + wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); + + wlr_scene_node_for_each_buffer(&view->content_tree->node, + view_save_buffer_iterator, view->saved_surface_tree); + + wlr_scene_node_set_enabled(&view->content_tree->node, false); + wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); } bool view_is_transient_for(struct sway_view *child, @@ -1202,3 +1212,19 @@ bool view_is_transient_for(struct sway_view *child, return child->impl->is_transient_for && child->impl->is_transient_for(child, ancestor); } + +static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, + int x, int y, void *data) { + struct timespec *when = data; + wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); +} + +void view_send_frame_done(struct sway_view *view) { + struct timespec when; + clock_gettime(CLOCK_MONOTONIC, &when); + + struct wlr_scene_node *node; + wl_list_for_each(node, &view->content_tree->children, link) { + wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); + } +} From 1e018e72b4d57c8f354b9be9686a7a75797cdcab Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 6 Apr 2023 22:23:53 +0200 Subject: [PATCH 24/36] Delete old damage tracking code The new scene graph abstraction handles this for us. --- include/sway/desktop.h | 13 -- include/sway/output.h | 29 --- include/sway/tree/container.h | 7 - include/sway/tree/view.h | 18 -- sway/commands/client.c | 5 - sway/commands/opacity.c | 1 - sway/commands/show_marks.c | 5 - sway/commands/title_align.c | 5 - sway/commands/titlebar_border_thickness.c | 1 - sway/commands/titlebar_padding.c | 1 - sway/config/output.c | 4 - sway/desktop/desktop.c | 40 ---- sway/desktop/output.c | 240 ---------------------- sway/desktop/transaction.c | 18 -- sway/desktop/xdg_shell.c | 20 -- sway/input/seatop_move_floating.c | 3 - sway/meson.build | 1 - sway/tree/container.c | 6 - sway/tree/view.c | 30 --- sway/tree/workspace.c | 1 - 20 files changed, 448 deletions(-) delete mode 100644 include/sway/desktop.h delete mode 100644 sway/desktop/desktop.c diff --git a/include/sway/desktop.h b/include/sway/desktop.h deleted file mode 100644 index 7f2f5b3eb..000000000 --- a/include/sway/desktop.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -struct sway_container; -struct sway_view; - -void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, - bool whole); - -void desktop_damage_whole_container(struct sway_container *con); - -void desktop_damage_box(struct wlr_box *box); - -void desktop_damage_view(struct sway_view *view); diff --git a/include/sway/output.h b/include/sway/output.h index e20233061..30595f545 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -45,8 +45,6 @@ struct sway_output { struct wlr_box usable_area; - struct wlr_damage_ring damage_ring; - int lx, ly; // layout coords int width, height; // transformed buffer size enum wl_output_subpixel detected_subpixel; @@ -99,19 +97,6 @@ typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, void *user_data); -void output_damage_whole(struct sway_output *output); - -void output_damage_surface(struct sway_output *output, double ox, double oy, - struct wlr_surface *surface, bool whole); - -void output_damage_from_view(struct sway_output *output, - struct sway_view *view); - -void output_damage_box(struct sway_output *output, struct wlr_box *box); - -void output_damage_whole_container(struct sway_output *output, - struct sway_container *con); - bool output_match_name_or_id(struct sway_output *output, const char *name_or_id); @@ -129,18 +114,6 @@ void output_disable(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, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data); - -void output_view_for_each_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data); - -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_for_each_workspace(struct sway_output *output, void (*f)(struct sway_workspace *ws, void *data), void *data); @@ -158,8 +131,6 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void scale_box(struct wlr_box *box, float scale); - enum wlr_direction opposite_direction(enum wlr_direction d); void handle_output_layout_change(struct wl_listener *listener, void *data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4920e064b..4cd4c8478 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -125,11 +125,6 @@ struct sway_container { double child_total_width; double child_total_height; - // In most cases this is the same as the content x and y, but if the view - // refuses to resize to the content dimensions then it can be smaller. - // These are in layout coordinates. - double surface_x, surface_y; - // Outputs currently being intersected list_t *outputs; // struct sway_output @@ -181,8 +176,6 @@ bool container_has_ancestor(struct sway_container *container, void container_update_textures_recursive(struct sway_container *con); -void container_damage_whole(struct sway_container *container); - void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 66d6db1c4..80097dd3e 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -46,10 +46,6 @@ struct sway_view_impl { void (*set_fullscreen)(struct sway_view *view, bool fullscreen); void (*set_resizing)(struct sway_view *view, bool resizing); bool (*wants_floating)(struct sway_view *view); - void (*for_each_surface)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - void (*for_each_popup_surface)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); bool (*is_transient_for)(struct sway_view *child, struct sway_view *ancestor); void (*close)(struct sway_view *view); @@ -254,20 +250,6 @@ void view_close(struct sway_view *view); void view_close_popups(struct sway_view *view); -void view_damage_from(struct sway_view *view); - -/** - * Iterate all surfaces of a view (toplevels + popups). - */ -void view_for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - -/** - * Iterate all popup surfaces of a view. - */ -void view_for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - // view implementation bool view_init(struct sway_view *view, enum sway_view_type type, diff --git a/sway/commands/client.c b/sway/commands/client.c index 7a9cff2c7..fd2ac7a87 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -51,11 +51,6 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, if (config->active) { root_for_each_container(container_update_iterator, NULL); - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 96e6228ed..1c44a6468 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -37,6 +37,5 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { } con->alpha = val; - container_damage_whole(con); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index f738144fe..fecb5ade4 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -26,10 +26,5 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { root_for_each_container(title_bar_update_iterator, NULL); } - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } - return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index 7f5dd3ae5..be298a290 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c @@ -27,10 +27,5 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { root_for_each_container(arrange_title_bar_iterator, NULL); - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } - return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 7c27c163a..fa3db3c50 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c @@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { "Expected output to have a workspace"); } arrange_workspace(ws); - output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index 29ce59ff0..6999f7a27 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c @@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; arrange_workspace(output_get_active_workspace(output)); - output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/config/output.c b/sway/config/output.c index 3316085a7..66baf194f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -538,10 +538,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { return true; } - if (config->reloading) { - output_damage_whole(output); - } - if (oc) { enum scale_filter_mode scale_filter_old = output->scale_filter; switch (oc->scale_filter) { diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index c8d4502cd..000000000 --- a/sway/desktop/desktop.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "sway/tree/container.h" -#include "sway/desktop.h" -#include "sway/output.h" - -void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, - bool whole) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, - output->wlr_output, &output_box); - output_damage_surface(output, lx - output_box.x, - ly - output_box.y, surface, whole); - } -} - -void desktop_damage_whole_container(struct sway_container *con) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole_container(output, con); - } -} - -void desktop_damage_box(struct wlr_box *box) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_box(output, box); - } -} - -void desktop_damage_view(struct sway_view *view) { - desktop_damage_whole_container(view->container); - struct wlr_box box = { - .x = view->container->current.content_x - view->geometry.x, - .y = view->container->current.content_y - view->geometry.y, - .width = view->surface->current.width, - .height = view->surface->current.height, - }; - desktop_damage_box(&box); -} diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 36c8f52ce..aea2a8d79 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -71,127 +71,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) { return NULL; } -struct surface_iterator_data { - sway_surface_iterator_func_t user_iterator; - void *user_data; - - struct sway_output *output; - struct sway_view *view; - double ox, oy; - int width, height; -}; - -static bool get_surface_box(struct surface_iterator_data *data, - struct wlr_surface *surface, int sx, int sy, - struct wlr_box *surface_box) { - struct sway_output *output = data->output; - - if (!wlr_surface_has_buffer(surface)) { - return false; - } - - int sw = surface->current.width; - int sh = surface->current.height; - - struct wlr_box box = { - .x = floor(data->ox + sx), - .y = floor(data->oy + sy), - .width = sw, - .height = sh, - }; - if (surface_box != NULL) { - memcpy(surface_box, &box, sizeof(struct wlr_box)); - } - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - return wlr_box_intersection(&intersection, &output_box, &box); -} - -static void output_for_each_surface_iterator(struct wlr_surface *surface, - int sx, int sy, void *_data) { - struct surface_iterator_data *data = _data; - - struct wlr_box box; - bool intersects = get_surface_box(data, surface, sx, sy, &box); - if (!intersects) { - return; - } - - data->user_iterator(data->output, data->view, surface, &box, - data->user_data); -} - -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = ox, - .oy = oy, - .width = surface->current.width, - .height = surface->current.height, - }; - - wlr_surface_for_each_surface(surface, - output_for_each_surface_iterator, &data); -} - -void output_view_for_each_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = view, - .ox = view->container->surface_x - output->lx - - view->geometry.x, - .oy = view->container->surface_y - output->ly - - view->geometry.y, - .width = view->container->current.content_width, - .height = view->container->current.content_height, - }; - - view_for_each_surface(view, output_for_each_surface_iterator, &data); -} - -void output_view_for_each_popup_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = view, - .ox = view->container->surface_x - output->lx - - view->geometry.x, - .oy = view->container->surface_y - output->ly - - view->geometry.y, - .width = view->container->current.content_width, - .height = view->container->current.content_height, - }; - - view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); -} - -static int scale_length(int length, int offset, float scale) { - return roundf((offset + length) * scale) - roundf(offset * scale); -} - -void scale_box(struct wlr_box *box, float scale) { - box->width = scale_length(box->width, box->x, scale); - box->height = scale_length(box->height, box->y, scale); - box->x = roundf(box->x * scale); - box->y = roundf(box->y * scale); -} struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); @@ -339,9 +218,6 @@ static int output_repaint_timer_handler(void *data) { } wlr_scene_output_commit(output->scene_output, NULL); - - wlr_damage_ring_rotate(&output->damage_ring); - return 0; } @@ -409,107 +285,6 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); } -void output_damage_whole(struct sway_output *output) { - // The output can exist with no wlr_output if it's just been disconnected - // and the transaction to evacuate it has't completed yet. - if (output != NULL && output->wlr_output != NULL) { - wlr_damage_ring_add_whole(&output->damage_ring); - wlr_output_schedule_frame(output->wlr_output); - } -} - -static void damage_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - bool *data = _data; - bool whole = *data; - - struct wlr_box box = *_box; - scale_box(&box, output->wlr_output->scale); - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_surface_get_effective_damage(surface, &damage); - wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceilf(output->wlr_output->scale) > surface->current.scale) { - // When scaling up a surface, it'll become blurry so we need to - // expand the damage region - wlr_region_expand(&damage, &damage, - ceilf(output->wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box.x, box.y); - if (wlr_damage_ring_add(&output->damage_ring, &damage)) { - wlr_output_schedule_frame(output->wlr_output); - } - pixman_region32_fini(&damage); - - if (whole) { - if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { - wlr_output_schedule_frame(output->wlr_output); - } - } - - if (!wl_list_empty(&surface->current.frame_callback_list)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - -void output_damage_surface(struct sway_output *output, double ox, double oy, - struct wlr_surface *surface, bool whole) { - output_surface_for_each_surface(output, surface, ox, oy, - damage_surface_iterator, &whole); -} - -void output_damage_from_view(struct sway_output *output, - struct sway_view *view) { - if (!view_is_visible(view)) { - return; - } - bool whole = false; - output_view_for_each_surface(output, view, damage_surface_iterator, &whole); -} - -// Expecting an unscaled box in layout coordinates -void output_damage_box(struct sway_output *output, struct wlr_box *_box) { - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx; - box.y -= output->ly; - scale_box(&box, output->wlr_output->scale); - if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - -static void damage_child_views_iterator(struct sway_container *con, - void *data) { - if (!con->view || !view_is_visible(con->view)) { - return; - } - struct sway_output *output = data; - bool whole = true; - output_view_for_each_surface(output, con->view, damage_surface_iterator, - &whole); -} - -void output_damage_whole_container(struct sway_output *output, - struct sway_container *con) { - // Pad the box by 1px, because the width is a double and might be a fraction - struct wlr_box box = { - .x = con->current.x - output->lx - 1, - .y = con->current.y - output->ly - 1, - .width = con->current.width + 2, - .height = con->current.height + 2, - }; - scale_box(&box, output->wlr_output->scale); - // Damage subsurfaces as well, which may extend outside the box - if (con->view) { - damage_child_views_iterator(con, output); - } else { - container_for_each_child(con, damage_child_views_iterator, output); - } -} - static void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -553,8 +328,6 @@ static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); - wlr_damage_ring_finish(&output->damage_ring); - wlr_scene_output_destroy(output->scene_output); output->scene_output = NULL; output->wlr_output->data = NULL; @@ -594,15 +367,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM)) { - int width, height; - wlr_output_transformed_resolution(output->wlr_output, &width, &height); - wlr_damage_ring_set_bounds(&output->damage_ring, width, height); - wlr_output_schedule_frame(output->wlr_output); - } - // Next time the output is enabled, try to re-apply the gamma LUT if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { output->gamma_lut_changed = true; @@ -684,7 +448,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->server = server; output->scene_output = scene_output; - wlr_damage_ring_init(&output->damage_ring); wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); output->layout_destroy.notify = handle_layout_destroy; @@ -712,9 +475,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { transaction_commit_dirty(); - int width, height; - wlr_output_transformed_resolution(output->wlr_output, &width, &height); - wlr_damage_ring_set_bounds(&output->damage_ring, width, height); update_output_manager_config(server); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 1ae6642bb..5f104aa34 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -5,7 +5,6 @@ #include #include #include "sway/config.h" -#include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -214,19 +213,15 @@ static void transaction_add_node(struct sway_transaction *transaction, static void apply_output_state(struct sway_output *output, struct sway_output_state *state) { - output_damage_whole(output); list_free(output->current.workspaces); memcpy(&output->current, state, sizeof(struct sway_output_state)); - output_damage_whole(output); } static void apply_workspace_state(struct sway_workspace *ws, struct sway_workspace_state *state) { - output_damage_whole(ws->current.output); list_free(ws->current.floating); list_free(ws->current.tiling); memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); - output_damage_whole(ws->current.output); } static void apply_container_state(struct sway_container *container, @@ -256,19 +251,6 @@ static void apply_container_state(struct sway_container *container, } } - // Damage the new location - desktop_damage_whole_container(container); - if (view && view->surface) { - struct wlr_surface *surface = view->surface; - struct wlr_box box = { - .x = container->current.content_x - view->geometry.x, - .y = container->current.content_y - view->geometry.y, - .width = surface->current.width, - .height = surface->current.height, - }; - desktop_damage_box(&box); - } - if (!container->node.destroying) { container_discover_outputs(container); } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 95b5cb9d7..48b7b4c7e 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -214,24 +214,6 @@ static bool wants_floating(struct sway_view *view) { || toplevel->parent; } -static void for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, - user_data); -} - -static void for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, - iterator, user_data); -} - static bool is_transient_for(struct sway_view *child, struct sway_view *ancestor) { if (xdg_shell_view_from_view(child) == NULL) { @@ -279,8 +261,6 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .set_resizing = set_resizing, .wants_floating = wants_floating, - .for_each_surface = for_each_surface, - .for_each_popup_surface = for_each_popup_surface, .is_transient_for = is_transient_for, .close = _close, .close_popups = close_popups, diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index ddcd4c53e..21d048ce2 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -1,6 +1,5 @@ #define _POSIX_C_SOURCE 200809L #include -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -39,9 +38,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_floating_event *e = seat->seatop_data; struct wlr_cursor *cursor = seat->cursor->cursor; - desktop_damage_whole_container(e->con); container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); - desktop_damage_whole_container(e->con); transaction_commit_dirty(); } diff --git a/sway/meson.build b/sway/meson.build index 110de58c3..1079c7494 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -15,7 +15,6 @@ sway_sources = files( 'xdg_activation_v1.c', 'xdg_decoration.c', - 'desktop/desktop.c', 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', diff --git a/sway/tree/container.c b/sway/tree/container.c index 4aae4d327..cde9dff5d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -597,12 +597,6 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } -void container_damage_whole(struct sway_container *container) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole_container(output, container); - } -} /** * Return the output which will be used for scale purposes. diff --git a/sway/tree/view.c b/sway/tree/view.c index 402fa179e..2874e88b9 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -16,7 +16,6 @@ #include "log.h" #include "sway/criteria.h" #include "sway/commands.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/launcher.h" @@ -456,34 +455,6 @@ void view_close_popups(struct sway_view *view) { } } -void view_damage_from(struct sway_view *view) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_from_view(output, view); - } -} - -void view_for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_surface) { - view->impl->for_each_surface(view, iterator, user_data); - } else { - wlr_surface_for_each_surface(view->surface, iterator, user_data); - } -} - -void view_for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_popup_surface) { - view->impl->for_each_popup_surface(view, iterator, user_data); - } -} static bool view_has_executed_criteria(struct sway_view *view, struct criteria *criteria) { for (int i = 0; i < view->executed_criteria->length; ++i) { @@ -1143,7 +1114,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { view->urgent_timer = NULL; } } - container_damage_whole(view->container); ipc_event_window(view->container, "urgent"); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index f60b23662..40d33435c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -686,7 +686,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { if (workspace->urgent != new_urgent) { workspace->urgent = new_urgent; ipc_event_workspace(NULL, workspace, "urgent"); - output_damage_whole(workspace->output); } } From 5f0801b6f245cc789ea93b9449dd0c573ef36e66 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 21 Feb 2023 20:58:17 -0500 Subject: [PATCH 25/36] container: Don't track outputs The scene graph abstraction does this for us --- include/sway/surface.h | 10 ----- include/sway/tree/container.h | 12 ------ sway/desktop/layer_shell.c | 1 - sway/desktop/surface.c | 31 --------------- sway/desktop/transaction.c | 4 -- sway/lock.c | 1 - sway/meson.build | 1 - sway/tree/container.c | 72 ----------------------------------- sway/tree/output.c | 10 ----- sway/tree/view.c | 1 - 10 files changed, 143 deletions(-) delete mode 100644 include/sway/surface.h delete mode 100644 sway/desktop/surface.c diff --git a/include/sway/surface.h b/include/sway/surface.h deleted file mode 100644 index 81eb80d59..000000000 --- a/include/sway/surface.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _SWAY_SURFACE_H -#define _SWAY_SURFACE_H -#include - -void surface_enter_output(struct wlr_surface *surface, - struct sway_output *output); -void surface_leave_output(struct wlr_surface *surface, - struct sway_output *output); - -#endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4cd4c8478..37a6b2b3c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -125,9 +125,6 @@ struct sway_container { double child_total_width; double child_total_height; - // Outputs currently being intersected - list_t *outputs; // struct sway_output - // Indicates that the container is a scratchpad container. // Both hidden and visible scratchpad containers have scratchpad=true. // Hidden scratchpad containers have a NULL parent. @@ -280,15 +277,6 @@ bool container_is_floating_or_child(struct sway_container *container); */ bool container_is_fullscreen_or_child(struct sway_container *container); -/** - * Return the output which will be used for scale purposes. - * This is the most recently entered output. - * If the container is not on any output, return NULL. - */ -struct sway_output *container_get_effective_output(struct sway_container *con); - -void container_discover_outputs(struct sway_container *con); - enum sway_container_layout container_parent_layout(struct sway_container *con); list_t *container_get_siblings(struct sway_container *container); diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index a52d27fa5..aca99c97c 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -14,7 +14,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" #include diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index af17a8bb6..000000000 --- a/sway/desktop/surface.c +++ /dev/null @@ -1,31 +0,0 @@ -#define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include "sway/server.h" -#include "sway/surface.h" -#include "sway/output.h" - -static void surface_update_outputs(struct wlr_surface *surface) { - float scale = 1; - struct wlr_surface_output *surface_output; - wl_list_for_each(surface_output, &surface->current_outputs, link) { - if (surface_output->output->scale > scale) { - scale = surface_output->output->scale; - } - } - wlr_fractional_scale_v1_notify_scale(surface, scale); - wlr_surface_set_preferred_buffer_scale(surface, ceil(scale)); -} - -void surface_enter_output(struct wlr_surface *surface, - struct sway_output *output) { - wlr_surface_send_enter(surface, output->wlr_output); - surface_update_outputs(surface); -} - -void surface_leave_output(struct wlr_surface *surface, - struct sway_output *output) { - wlr_surface_send_leave(surface, output->wlr_output); - surface_update_outputs(surface); -} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 5f104aa34..ba9d06487 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -250,10 +250,6 @@ static void apply_container_state(struct sway_container *container, view_center_surface(view); } } - - if (!container->node.destroying) { - container_discover_outputs(container); - } } /** diff --git a/sway/lock.c b/sway/lock.c index 2856ae676..8ad9c3f62 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -8,7 +8,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/surface.h" struct sway_session_lock_output { struct wlr_scene_tree *tree; diff --git a/sway/meson.build b/sway/meson.build index 1079c7494..d937e4256 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -18,7 +18,6 @@ sway_sources = files( 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', - 'desktop/surface.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', 'desktop/launcher.c', diff --git a/sway/tree/container.c b/sway/tree/container.c index cde9dff5d..b19081fc7 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -17,7 +17,6 @@ #include "sway/sway_text_node.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -113,7 +112,6 @@ struct sway_container *container_create(struct sway_view *view) { c->view = view; c->alpha = 1.0f; c->marks = create_list(); - c->outputs = create_list(); wl_signal_init(&c->events.destroy); wl_signal_emit_mutable(&root->events.new_node, &c->node); @@ -453,7 +451,6 @@ void container_destroy(struct sway_container *con) { free(con->formatted_title); list_free(con->pending.children); list_free(con->current.children); - list_free(con->outputs); list_free_items_and_destroy(con->marks); @@ -597,18 +594,6 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } - -/** - * Return the output which will be used for scale purposes. - * This is the most recently entered output. - */ -struct sway_output *container_get_effective_output(struct sway_container *con) { - if (con->outputs->length == 0) { - return NULL; - } - return con->outputs->items[con->outputs->length - 1]; -} - /** * Calculate and return the length of the tree representation. * An example tree representation is: V[Terminal, Firefox] @@ -1267,63 +1252,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { return false; } -static void surface_send_enter_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct sway_output *output = data; - surface_enter_output(surface, output); -} - -static void surface_send_leave_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct sway_output *output = data; - surface_leave_output(surface, output); -} - -void container_discover_outputs(struct sway_container *con) { - struct wlr_box con_box = { - .x = con->current.x, - .y = con->current.y, - .width = con->current.width, - .height = con->current.height, - }; - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - struct wlr_box output_box; - output_get_box(output, &output_box); - struct wlr_box intersection; - bool intersects = - wlr_box_intersection(&intersection, &con_box, &output_box); - int index = list_find(con->outputs, output); - - if (intersects && index == -1) { - // Send enter - sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); - if (con->view) { - view_for_each_surface(con->view, - surface_send_enter_iterator, output); - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_enter( - con->view->foreign_toplevel, output->wlr_output); - } - } - list_add(con->outputs, output); - } else if (!intersects && index != -1) { - // Send leave - sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); - if (con->view) { - view_for_each_surface(con->view, - surface_send_leave_iterator, output); - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_leave( - con->view->foreign_toplevel, output->wlr_output); - } - } - list_del(con->outputs, index); - } - } -} - enum sway_container_layout container_parent_layout(struct sway_container *con) { if (con->pending.parent) { return con->pending.parent->pending.layout; diff --git a/sway/tree/output.c b/sway/tree/output.c index 3c8823dc9..cd7bf0c27 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -283,14 +283,6 @@ void output_destroy(struct sway_output *output) { free(output); } -static void untrack_output(struct sway_container *con, void *data) { - struct sway_output *output = data; - int index = list_find(con->outputs, output); - if (index != -1) { - list_del(con->outputs, index); - } -} - void output_disable(struct sway_output *output) { if (!sway_assert(output->enabled, "Expected an enabled output")) { return; @@ -305,8 +297,6 @@ void output_disable(struct sway_output *output) { output_evacuate(output); - root_for_each_container(untrack_output, output); - list_del(root->outputs, index); output->enabled = false; diff --git a/sway/tree/view.c b/sway/tree/view.c index 2874e88b9..3bc0855bc 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,7 +25,6 @@ #include "sway/input/seat.h" #include "sway/scene_descriptor.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/sway_text_node.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" From 2e53de80bb0f4c93e74ae050fa07e78f18e909d9 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 18 Jan 2024 10:01:12 -0500 Subject: [PATCH 26/36] scene_graph: Arrange scene graph on transaction apply --- sway/desktop/transaction.c | 430 ++++++++++++++++++++++++++++++++++++- 1 file changed, 428 insertions(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ba9d06487..0755c8a00 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -5,6 +5,7 @@ #include #include #include "sway/config.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -252,6 +253,431 @@ static void apply_container_state(struct sway_container *container, } } +static void arrange_title_bar(struct sway_container *con, + int x, int y, int width, int height) { + container_update(con); + + bool has_title_bar = height > 0; + wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); + if (!has_title_bar) { + return; + } + + wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); + + con->title_width = width; + container_arrange_title_bar(con); +} + +static void disable_container(struct sway_container *con) { + if (con->view) { + wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); + } else { + for (int i = 0; i < con->current.children->length; i++) { + struct sway_container *child = con->current.children->items[i]; + + wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); + + disable_container(child); + } + } +} + +static void arrange_container(struct sway_container *con, + int width, int height, bool title_bar, int gaps); + +static void arrange_children(enum sway_container_layout layout, list_t *children, + struct sway_container *active, struct wlr_scene_tree *content, + int width, int height, int gaps) { + int title_bar_height = container_titlebar_height(); + + if (layout == L_TABBED) { + struct sway_container *first = children->length == 1 ? + ((struct sway_container *)children->items[0]) : NULL; + if (config->hide_lone_tab && first && first->view && + first->current.border != B_NORMAL) { + title_bar_height = 0; + } + + double w = (double) width / children->length; + int title_offset = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + bool activated = child == active; + int next_title_offset = round(w * i + w); + + arrange_title_bar(child, title_offset, -title_bar_height, + next_title_offset - title_offset, title_bar_height); + wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); + wlr_scene_node_reparent(&child->scene_tree->node, content); + + if (activated) { + arrange_container(child, width, height - title_bar_height, + false, 0); + } else { + disable_container(child); + } + + title_offset = next_title_offset; + } + } else if (layout == L_STACKED) { + struct sway_container *first = children->length == 1 ? + ((struct sway_container *)children->items[0]) : NULL; + if (config->hide_lone_tab && first && first->view && + first->current.border != B_NORMAL) { + title_bar_height = 0; + } + + int title_height = title_bar_height * children->length; + + int y = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + bool activated = child == active; + + arrange_title_bar(child, 0, y - title_height, width, title_bar_height); + wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); + wlr_scene_node_reparent(&child->scene_tree->node, content); + + if (activated) { + arrange_container(child, width, height - title_height, + false, 0); + } else { + disable_container(child); + } + + y += title_bar_height; + } + } else if (layout == L_VERT) { + int off = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + int cheight = child->current.height; + + wlr_scene_node_set_enabled(&child->border.tree->node, true); + wlr_scene_node_set_position(&child->scene_tree->node, 0, off); + wlr_scene_node_reparent(&child->scene_tree->node, content); + arrange_container(child, width, cheight, true, gaps); + off += cheight + gaps; + } + } else if (layout == L_HORIZ) { + int off = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + int cwidth = child->current.width; + + wlr_scene_node_set_enabled(&child->border.tree->node, true); + wlr_scene_node_set_position(&child->scene_tree->node, off, 0); + wlr_scene_node_reparent(&child->scene_tree->node, content); + arrange_container(child, cwidth, height, true, gaps); + off += cwidth + gaps; + } + } else { + sway_assert(false, "unreachable"); + } +} + +static void arrange_container(struct sway_container *con, + int width, int height, bool title_bar, int gaps) { + // this container might have previously been in the scratchpad, + // make sure it's enabled for viewing + wlr_scene_node_set_enabled(&con->scene_tree->node, true); + + if (con->view) { + int border_top = container_titlebar_height(); + int border_width = con->current.border_thickness; + + if (title_bar && con->current.border != B_NORMAL) { + wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); + wlr_scene_node_set_enabled(&con->border.top->node, true); + } else { + wlr_scene_node_set_enabled(&con->border.top->node, false); + } + + if (con->current.border == B_NORMAL) { + if (title_bar) { + arrange_title_bar(con, 0, 0, width, border_top); + } else { + border_top = 0; + // should be handled by the parent container + } + } else if (con->current.border == B_PIXEL) { + container_update(con); + border_top = title_bar && con->current.border_top ? border_width : 0; + } else if (con->current.border == B_NONE) { + container_update(con); + border_top = 0; + border_width = 0; + } else if (con->current.border == B_CSD) { + border_top = 0; + border_width = 0; + } else { + sway_assert(false, "unreachable"); + } + + int border_bottom = con->current.border_bottom ? border_width : 0; + int border_left = con->current.border_left ? border_width : 0; + int border_right = con->current.border_right ? border_width : 0; + + wlr_scene_rect_set_size(con->border.top, width, border_top); + wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); + wlr_scene_rect_set_size(con->border.left, + border_left, height - border_top - border_bottom); + wlr_scene_rect_set_size(con->border.right, + border_right, height - border_top - border_bottom); + + wlr_scene_node_set_position(&con->border.top->node, 0, 0); + wlr_scene_node_set_position(&con->border.bottom->node, + 0, height - border_bottom); + wlr_scene_node_set_position(&con->border.left->node, + 0, border_top); + wlr_scene_node_set_position(&con->border.right->node, + width - border_right, border_top); + + // make sure to reparent, it's possible that the client just came out of + // fullscreen mode where the parent of the surface is not the container + wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); + wlr_scene_node_set_position(&con->view->scene_tree->node, + border_left, border_top); + } else { + // make sure to disable the title bar if the parent is not managing it + if (title_bar) { + wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); + } + + arrange_children(con->current.layout, con->current.children, + con->current.focused_inactive_child, con->content_tree, + width, height, gaps); + } +} + +static int container_get_gaps(struct sway_container *con) { + struct sway_workspace *ws = con->current.workspace; + struct sway_container *temp = con; + while (temp) { + enum sway_container_layout layout; + if (temp->current.parent) { + layout = temp->current.parent->current.layout; + } else { + layout = ws->current.layout; + } + if (layout == L_TABBED || layout == L_STACKED) { + return 0; + } + temp = temp->pending.parent; + } + return ws->gaps_inner; +} + +static void arrange_fullscreen(struct wlr_scene_tree *tree, + struct sway_container *fs, struct sway_workspace *ws, + int width, int height) { + struct wlr_scene_node *fs_node; + if (fs->view) { + fs_node = &fs->view->scene_tree->node; + + // if we only care about the view, disable any decorations + wlr_scene_node_set_enabled(&fs->scene_tree->node, false); + } else { + fs_node = &fs->scene_tree->node; + arrange_container(fs, width, height, true, container_get_gaps(fs)); + } + + wlr_scene_node_reparent(fs_node, tree); + wlr_scene_node_lower_to_bottom(fs_node); + wlr_scene_node_set_position(fs_node, 0, 0); +} + +static void arrange_workspace_floating(struct sway_workspace *ws) { + for (int i = 0; i < ws->current.floating->length; i++) { + struct sway_container *floater = ws->current.floating->items[i]; + struct wlr_scene_tree *layer = root->layers.floating; + + if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { + continue; + } + + if (root->fullscreen_global) { + if (container_is_transient_for(floater, root->fullscreen_global)) { + layer = root->layers.fullscreen_global; + } + } else { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + struct sway_workspace *active = output->current.active_workspace; + + if (active && active->fullscreen && + container_is_transient_for(floater, active->fullscreen)) { + layer = root->layers.fullscreen; + } + } + } + + wlr_scene_node_reparent(&floater->scene_tree->node, layer); + wlr_scene_node_set_position(&floater->scene_tree->node, + floater->current.x, floater->current.y); + wlr_scene_node_set_enabled(&floater->scene_tree->node, true); + + arrange_container(floater, floater->current.width, floater->current.height, + true, ws->gaps_inner); + } +} + +static void arrange_workspace_tiling(struct sway_workspace *ws, + int width, int height) { + arrange_children(ws->current.layout, ws->current.tiling, + ws->current.focused_inactive_child, ws->layers.tiling, + width, height, ws->gaps_inner); +} + +static void disable_workspace(struct sway_workspace *ws) { + // if any containers were just moved to a disabled workspace it will + // have the parent of the old workspace. Move the workspace so that it won't + // be shown. + for (int i = 0; i < ws->current.tiling->length; i++) { + struct sway_container *child = ws->current.tiling->items[i]; + + wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); + disable_container(child); + } + + for (int i = 0; i < ws->current.floating->length; i++) { + struct sway_container *floater = ws->current.floating->items[i]; + wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); + disable_container(floater); + wlr_scene_node_set_enabled(&floater->scene_tree->node, false); + } +} + +static void arrange_output(struct sway_output *output, int width, int height) { + for (int i = 0; i < output->current.workspaces->length; i++) { + struct sway_workspace *child = output->current.workspaces->items[i]; + + bool activated = output->current.active_workspace == child; + + wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); + wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); + + for (int i = 0; i < child->current.floating->length; i++) { + struct sway_container *floater = child->current.floating->items[i]; + wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); + wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); + } + + if (activated) { + struct sway_container *fs = child->current.fullscreen; + wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); + wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); + + arrange_workspace_floating(child); + + wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); + wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); + wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); + + if (fs) { + wlr_scene_rect_set_size(output->fullscreen_background, width, height); + + arrange_fullscreen(child->layers.fullscreen, fs, child, + width, height); + } else { + struct wlr_box *area = &output->usable_area; + struct side_gaps *gaps = &child->current_gaps; + + wlr_scene_node_set_position(&child->layers.tiling->node, + gaps->left + area->x, gaps->top + area->y); + + arrange_workspace_tiling(child, + area->width - gaps->left - gaps->right, + area->height - gaps->top - gaps->bottom); + } + } else { + wlr_scene_node_set_enabled(&child->layers.tiling->node, false); + wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); + + disable_workspace(child); + } + } +} + +static void arrange_popup(struct wlr_scene_tree *popup) { + struct wlr_scene_node *node; + wl_list_for_each(node, &popup->children, link) { + struct sway_xdg_popup *popup = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_POPUP); + + // the popup layer may have popups from layer_shell surfaces, in this + // case those don't have a scene descriptor, so lets skip those here. + if (popup) { + struct wlr_scene_tree *tree = popup->view->content_tree; + + int lx, ly; + wlr_scene_node_coords(&tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); + } + } +} + +static void arrange_root(struct sway_root *root) { + struct sway_container *fs = root->fullscreen_global; + + wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); + wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); + wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); + wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); + + // hide all contents in the scratchpad + for (int i = 0; i < root->scratchpad->length; i++) { + struct sway_container *con = root->scratchpad->items[i]; + + wlr_scene_node_set_enabled(&con->scene_tree->node, false); + } + + if (fs) { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + struct sway_workspace *ws = output->current.active_workspace; + + if (ws) { + arrange_workspace_floating(ws); + } + } + + arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, + root->width, root->height); + } else { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + + wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); + + wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); + wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); + wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); + wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); + wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); + wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); + wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); + + wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); + + arrange_output(output, output->width, output->height); + } + } + + arrange_popup(root->layers.popup); +} + /** * Apply a transaction to the "current" state of the tree. */ @@ -291,8 +717,6 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - - cursor_rebase_all(); } static void transaction_commit_pending(void); @@ -305,6 +729,8 @@ static void transaction_progress(void) { return; } transaction_apply(server.queued_transaction); + arrange_root(root); + cursor_rebase_all(); transaction_destroy(server.queued_transaction); server.queued_transaction = NULL; From 09e11dabb203513c038b723e50de2fd836e7edb3 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 16 Aug 2023 17:45:01 -0400 Subject: [PATCH 27/36] scene_graph: Port opacity and filter modes --- sway/commands/opacity.c | 2 ++ sway/config/output.c | 1 + sway/desktop/output.c | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 1c44a6468..610cecc6d 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -37,5 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { } con->alpha = val; + container_update(con); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config/output.c b/sway/config/output.c index 66baf194f..1a5215fe8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -554,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { if (scale_filter_old != output->scale_filter) { sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, sway_output_scale_filter_to_string(output->scale_filter)); + wlr_damage_ring_add_whole(&output->scene_output->damage_ring); } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index aea2a8d79..9c4baafdb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -182,6 +182,47 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, } } +static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { + switch (output->scale_filter) { + case SCALE_FILTER_LINEAR: + return WLR_SCALE_FILTER_BILINEAR; + case SCALE_FILTER_NEAREST: + return WLR_SCALE_FILTER_NEAREST; + default: + abort(); // unreachable + } +} + +static void output_configure_scene(struct sway_output *output, + struct wlr_scene_node *node, float opacity) { + if (!node->enabled) { + return; + } + + struct sway_container *con = + scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); + if (con) { + opacity = con->alpha; + } + + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + + // hack: don't call the scene setter because that will damage all outputs + // We don't want to damage outputs that aren't our current output that + // we're configuring + buffer->filter_mode = get_scale_filter(output); + + wlr_scene_buffer_set_opacity(buffer, opacity); + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + output_configure_scene(output, node, opacity); + } + } +} + static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; @@ -191,6 +232,8 @@ static int output_repaint_timer_handler(void *data) { output->wlr_output->frame_pending = false; + output_configure_scene(output, &root->root_scene->tree.node, 1.0f); + if (output->gamma_lut_changed) { struct wlr_output_state pending; wlr_output_state_init(&pending); From 9da295c11f90dcfbf254ccf23b9124c87ccd8ddf Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 22 Nov 2023 15:11:12 -0500 Subject: [PATCH 28/36] scene_graph: Implement toplevel clipping --- include/sway/tree/view.h | 2 +- sway/desktop/transaction.c | 2 +- sway/desktop/xdg_shell.c | 4 ++-- sway/desktop/xwayland.c | 4 ++-- sway/tree/view.c | 27 +++++++++++++++++++++------ 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 80097dd3e..3e5a9bfe3 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -274,7 +274,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, void view_unmap(struct sway_view *view); void view_update_size(struct sway_view *view); -void view_center_surface(struct sway_view *view); +void view_center_and_clip_surface(struct sway_view *view); struct sway_view *view_from_wlr_xdg_surface( struct wlr_xdg_surface *xdg_surface); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 0755c8a00..980e839ee 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -248,7 +248,7 @@ static void apply_container_state(struct sway_container *container, // the container. This is important for fullscreen views which // refuse to resize to the size of the output. if (view->surface) { - view_center_surface(view); + view_center_and_clip_surface(view); } } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 48b7b4c7e..7cdd97c83 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -306,9 +306,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { view->geometry.height); } transaction_commit_dirty_client(); - } else { - view_center_surface(view); } + + view_center_and_clip_surface(view); } if (view->container->node.instruction) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e0c80c074..9f3f4d5f2 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -436,9 +436,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { if (container_is_floating(view->container)) { view_update_size(view); transaction_commit_dirty_client(); - } else { - view_center_surface(view); } + + view_center_and_clip_surface(view); } if (view->container->node.instruction) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 3bc0855bc..d69841783 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -893,14 +893,29 @@ void view_update_size(struct sway_view *view) { container_set_geometry_from_content(con); } -void view_center_surface(struct sway_view *view) { +void view_center_and_clip_surface(struct sway_view *view) { struct sway_container *con = view->container; - // We always center the current coordinates rather than the next, as the - // geometry immediately affects the currently active rendering. - int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); - int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); - wlr_scene_node_set_position(&view->content_tree->node, x, y); + if (container_is_floating(con)) { + // We always center the current coordinates rather than the next, as the + // geometry immediately affects the currently active rendering. + int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); + int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); + + wlr_scene_node_set_position(&view->content_tree->node, x, y); + } else { + wlr_scene_node_set_position(&view->content_tree->node, 0, 0); + } + + // only make sure to clip the content if there is content to clip + if (!wl_list_empty(&con->view->content_tree->children)) { + wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ + .x = con->view->geometry.x, + .y = con->view->geometry.y, + .width = con->current.content_width, + .height = con->current.content_height, + }); + } } struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { From bab6b79af203762c10c31eaef3494dd4e4e4ac4f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 18 Mar 2022 08:42:30 -0400 Subject: [PATCH 29/36] Fix SIGSEGV on surface destroy ``` Program terminated with signal SIGSEGV, Segmentation fault. warning: Section `.reg-xstate/3960717' in core file too small. 0 container_get_siblings (container=0x55bcde4797f0) at ../sway/tree/container.c:1228 1228 if (list_find(container->pending.workspace->tiling, container) != -1) { [Current thread is 1 (Thread 0x7fa23b4a2940 (LWP 3960717))] (gdb) bt full= No symbol "full" in current context. (gdb) bt full 0 container_get_siblings (container=0x55bcde4797f0) at ../sway/tree/container.c:1228 1 0x000055bcdb62c704 in edge_is_external (cont=0x55bcde4797f0, edge=(WLR_EDGE_TOP | WLR_EDGE_LEFT)) at ../sway/input/seatop_default.c:54 siblings = 0x55bcde4797f0 index = 32766 layout = L_NONE __PRETTY_FUNCTION__ = "edge_is_external" 2 0x000055bcdb62c96f in find_resize_edge (cont=0x55bcde4797f0, surface=0x0, cursor=0x55bcddd5c2e0) at ../sway/input/seatop_default.c:106 edge = (WLR_EDGE_TOP | WLR_EDGE_LEFT) 3 0x000055bcdb620b3c in cursor_update_image (cursor=0x55bcddd5c2e0, node=0x55bcde4797f0) at ../sway/input/cursor.c:144 edge = WLR_EDGE_NONE 4 0x000055bcdb62eb8f in handle_rebase (seat=0x55bcddd5a740, time_msec=488992944) at ../sway/input/seatop_default.c:773 e = 0x55bcddd5c8e0 cursor = 0x55bcddd5c2e0 surface = 0x0 sx = 0 sy = 0 5 0x000055bcdb62c531 in seatop_rebase (seat=0x55bcddd5a740, time_msec=488992944) at ../sway/input/seat.c:1585 6 0x000055bcdb620a7d in cursor_rebase (cursor=0x55bcddd5c2e0) at ../sway/input/cursor.c:126 time_msec = 488992944 7 0x000055bcdb620ac4 in cursor_rebase_all () at ../sway/input/cursor.c:136 seat = 0x55bcddd5a740 8 0x000055bcdb61cc95 in transaction_apply (transaction=0x55bcde5b28c0) at ../sway/desktop/transaction.c:704 9 0x000055bcdb61ccdb in transaction_progress () at ../sway/desktop/transaction.c:716 10 0x000055bcdb61d1f9 in transaction_commit_pending () at ../sway/desktop/transaction.c:836 transaction = 0x55bcde5b28c0 11 0x000055bcdb61d596 in _transaction_commit_dirty (server_request=true) at ../sway/desktop/transaction.c:912 12 0x000055bcdb61d5ac in transaction_commit_dirty () at ../sway/desktop/transaction.c:916 13 0x000055bcdb65f579 in view_unmap (view=0x55bcde2ff180) at ../sway/tree/view.c:847 parent = 0x55bcde489010 ws = 0x55bcdde19080 seat = 0x55bcddd5a198 14 0x000055bcdb61e461 in handle_unmap (listener=0x55bcde2ff368, data=0x0) at ../sway/desktop/xdg_shell.c:394 xdg_shell_view = 0x55bcde2ff180 view = 0x55bcde2ff180 __PRETTY_FUNCTION__ = "handle_unmap" 15 0x00007fa23c4ae87f in wlr_signal_emit_safe (signal=0x55bcde46cf38, data=0x0) at ../util/signal.c:29 pos = 0x55bcde2ff368 l = 0x55bcde2ff368 cursor = {link = {prev = 0x55bcde2ff368, next = 0x7ffe240702a0}, notify = 0x7fa23c4ae7c9 } end = {link = {prev = 0x7ffe24070280, next = 0x55bcde46cf38}, notify = 0x7fa23c4ae7c9 } 16 0x00007fa23c47c3c7 in unmap_xdg_surface (surface=0x55bcde46ce30) at ../types/xdg_shell/wlr_xdg_surface.c:40 __PRETTY_FUNCTION__ = "unmap_xdg_surface" popup = 0x55bcde46ce60 popup_tmp = 0x55bcde46ce60 configure = 0x7ffe24070360 tmp = 0x55bcde488020 17 0x00007fa23c47cd47 in xdg_surface_role_precommit (wlr_surface=0x55bcde488020, state=0x55bcde4881a8) at ../types/xdg_shell/wlr_xdg_surface.c:330 surface = 0x55bcde46ce30 18 0x00007fa23c4813b2 in surface_commit_state (surface=0x55bcde488020, next=0x55bcde4881a8) at ../types/wlr_compositor.c:407 __PRETTY_FUNCTION__ = "surface_commit_state" invalid_buffer = false subsurface = 0xbd8e9aecae023300 --Type for more, q to quit, c to continue without paging-- 19 0x00007fa23c48192a in surface_handle_commit (client=0x55bcde488850, resource=0x55bcde2fdb80) at ../types/wlr_compositor.c:523 surface = 0x55bcde488020 20 0x00007fa23bb5ed4a in () at /usr/lib/libffi.so.8 21 0x00007fa23bb5e267 in () at /usr/lib/libffi.so.8 22 0x00007fa23c517323 in () at /usr/lib/libwayland-server.so.0 23 0x00007fa23c5125cc in () at /usr/lib/libwayland-server.so.0 24 0x00007fa23c5151ca in wl_event_loop_dispatch () at /usr/lib/libwayland-server.so.0 25 0x00007fa23c512d37 in wl_display_run () at /usr/lib/libwayland-server.so.0 26 0x000055bcdb616885 in server_run (server=0x55bcdb68c5c0 ) at ../sway/server.c:307 27 0x000055bcdb61594e in main (argc=3, argv=0x7ffe24070af8) at ../sway/main.c:433 ``` It seems to be happening because of this set of events all happening in the span of a single transaction: 1. You kill a tiled window that is the only window in a workplace. 2. Sway will destroy the workspace but not yet the container - this makes `con->pending.workspace` NULL. 3. Cursor glyphs get recomputed causing sway to recompute if the cursor is on a container edge. 4. That computation causes an access to the NULL workspace. Crash. --- sway/input/seatop_default.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 62261c776..0f4d03850 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -56,6 +56,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { while (cont) { if (container_parent_layout(cont) == layout) { list_t *siblings = container_get_siblings(cont); + if (!siblings) { + return false; + } int index = list_find(siblings, cont); if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { return false; From 7c635b61fe2efd122d131951c26f0c89d25f56cc Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 30 Apr 2022 17:09:44 -0400 Subject: [PATCH 30/36] remove damage debug options Now that we use wlr_scene, wlroots handles these. If available use the wlroots debug options instead. --- include/sway/server.h | 7 ------- sway/main.c | 8 +------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 33ffbf094..5df8a8e1b 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -156,13 +156,6 @@ struct sway_debug { bool noatomic; // Ignore atomic layout updates bool txn_timings; // Log verbose messages about transactions bool txn_wait; // Always wait for the timeout before applying - bool noscanout; // Disable direct scan-out - - enum { - DAMAGE_DEFAULT, // Default behaviour - DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged - DAMAGE_RERENDER, // Render the full output when any damage occurs - } damage; }; extern struct sway_debug debug; diff --git a/sway/main.c b/sway/main.c index 65c85d31f..21f19fc4b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -154,11 +154,7 @@ void restore_nofile_limit(void) { } void enable_debug_flag(const char *flag) { - if (strcmp(flag, "damage=highlight") == 0) { - debug.damage = DAMAGE_HIGHLIGHT; - } else if (strcmp(flag, "damage=rerender") == 0) { - debug.damage = DAMAGE_RERENDER; - } else if (strcmp(flag, "noatomic") == 0) { + if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; } else if (strcmp(flag, "txn-wait") == 0) { debug.txn_wait = true; @@ -166,8 +162,6 @@ void enable_debug_flag(const char *flag) { debug.txn_timings = true; } else if (strncmp(flag, "txn-timeout=", 12) == 0) { server.txn_timeout_ms = atoi(&flag[12]); - } else if (strcmp(flag, "noscanout") == 0) { - debug.noscanout = true; } else { sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); } From 5fc85c506687c0b0704e264e50299885e82d5602 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 14 Nov 2023 15:11:56 -0500 Subject: [PATCH 31/36] scene_graph: port wlr_forgein_toplevel_management output enter/leave events --- include/sway/tree/container.h | 4 +++ sway/desktop/transaction.c | 4 +++ sway/tree/container.c | 47 +++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 37a6b2b3c..93f6bfbb1 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -91,6 +91,10 @@ struct sway_container { } border; struct wlr_scene_tree *content_tree; + struct wlr_scene_buffer *output_handler; + + struct wl_listener output_enter; + struct wl_listener output_leave; struct sway_container_state current; struct sway_container_state pending; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 980e839ee..acc3e3f9c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -385,6 +385,10 @@ static void arrange_container(struct sway_container *con, // make sure it's enabled for viewing wlr_scene_node_set_enabled(&con->scene_tree->node, true); + if (con->output_handler) { + wlr_scene_buffer_set_dest_size(con->output_handler, width, height); + } + if (con->view) { int border_top = container_titlebar_height(); int border_width = con->current.border_thickness; diff --git a/sway/tree/container.c b/sway/tree/container.c index b19081fc7..30cb97bab 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -25,6 +25,35 @@ #include "log.h" #include "stringop.h" +static void handle_output_enter( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_enter); + struct wlr_scene_output *output = data; + + if (con->view->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_output_enter( + con->view->foreign_toplevel, output->output); + } +} + +static void handle_output_leave( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_leave); + struct wlr_scene_output *output = data; + + if (con->view->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_output_leave( + con->view->foreign_toplevel, output->output); + } +} + +static bool handle_point_accepts_input( + struct wlr_scene_buffer *buffer, double *x, double *y) { + return false; +} + static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, bool *failed) { if (*failed) { @@ -63,6 +92,7 @@ struct sway_container *container_create(struct sway_view *view) { // - content_tree (we put the content node here so when we disable the // border everything gets disabled. We only render the content iff there // is a border as well) + // - buffer used for output enter/leave events for foreign_toplevel bool failed = false; c->scene_tree = alloc_scene_tree(root->staging, &failed); @@ -90,6 +120,22 @@ struct sway_container *container_create(struct sway_view *view) { c->border.bottom = alloc_rect_node(c->border.tree, &failed); c->border.left = alloc_rect_node(c->border.tree, &failed); c->border.right = alloc_rect_node(c->border.tree, &failed); + + c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); + if (!c->output_handler) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + failed = true; + } + + if (!failed) { + c->output_enter.notify = handle_output_enter; + wl_signal_add(&c->output_handler->events.output_enter, + &c->output_enter); + c->output_leave.notify = handle_output_leave; + wl_signal_add(&c->output_handler->events.output_leave, + &c->output_leave); + c->output_handler->point_accepts_input = handle_point_accepts_input; + } } if (!failed && !scene_descriptor_assign(&c->scene_tree->node, @@ -456,6 +502,7 @@ void container_destroy(struct sway_container *con) { if (con->view && con->view->container == con) { con->view->container = NULL; + wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); } From ae33f4eb37a8ee647907e4fef72c6a488b8b1138 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 19 Jan 2024 12:21:15 +0100 Subject: [PATCH 32/36] Clarify gdk-pixbuf dependency purpose swaybg is out-of-tree so not relevant here. swaybar's tray doesn't actually depend on gdk-pixbuf, but gdk-pixbuf enables more image formats for swaybar tray when available. Closes: https://github.com/swaywm/sway/issues/7913 --- README.md | 2 +- meson_options.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf7e9c97f..15c7c0999 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Install dependencies: * json-c * pango * cairo -* gdk-pixbuf2 (optional: system tray) +* gdk-pixbuf2 (optional: additional image formats for system tray) * [swaybg] (optional: wallpaper) * [scdoc] (optional: man pages) \* * git (optional: version info) \* diff --git a/meson_options.txt b/meson_options.txt index 6ba675546..8d0d6509c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') -option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg') +option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') From 08a06a7b6bbb324e9fc6e49e96379340404135b4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 20 Jan 2024 10:57:01 +0100 Subject: [PATCH 33/36] Add debug flag to re-enable wl_drm 7e69a7076fc8 ("Drop wl_drm") has dropped wl_drm, however a lot of software wasn't quite ready for this (Xwayland, libva, amdvlk). Keep wl_drm disabled by default to pressure the wl_drm phase-out, but add a -Dlegacy-wl-drm flag for users to restore the previous behavior in the meantime. References: https://github.com/swaywm/sway/issues/7897 --- include/sway/server.h | 1 + sway/main.c | 2 ++ sway/server.c | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/include/sway/server.h b/include/sway/server.h index 5df8a8e1b..adb62cda7 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -156,6 +156,7 @@ struct sway_debug { bool noatomic; // Ignore atomic layout updates bool txn_timings; // Log verbose messages about transactions bool txn_wait; // Always wait for the timeout before applying + bool legacy_wl_drm; // Enable the legacy wl_drm interface }; extern struct sway_debug debug; diff --git a/sway/main.c b/sway/main.c index 21f19fc4b..73254dc22 100644 --- a/sway/main.c +++ b/sway/main.c @@ -162,6 +162,8 @@ void enable_debug_flag(const char *flag) { debug.txn_timings = true; } else if (strncmp(flag, "txn-timeout=", 12) == 0) { server.txn_timeout_ms = atoi(&flag[12]); + } else if (strcmp(flag, "legacy-wl-drm") == 0) { + debug.legacy_wl_drm = true; } else { sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); } diff --git a/sway/server.c b/sway/server.c index 33b25000b..73bf9d70e 100644 --- a/sway/server.c +++ b/sway/server.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -191,6 +192,10 @@ bool server_init(struct sway_server *server) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server->wl_display, 4, server->renderer); } + if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL && + debug.legacy_wl_drm) { + wlr_drm_create(server->wl_display, server->renderer); + } server->allocator = wlr_allocator_autocreate(server->backend, server->renderer); From 2c2625acd3a89ef2c4caa4094a3a51001041a524 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Sun, 21 Jan 2024 09:08:52 -0500 Subject: [PATCH 34/36] Fix SIGSEGV on output destroy ``` Program terminated with signal SIGSEGV, Segmentation fault. 144 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; [Current thread is 1 (Thread 0x7f1f7c5b3ac0 (LWP 2473))] (gdb) bt ``` Add a NULL check in `find_mapped_layer_by_client` like the one in `arrange_surface`. --- sway/desktop/layer_shell.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index aca99c97c..968b0cdb2 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -140,6 +140,9 @@ static struct sway_layer_surface *find_mapped_layer_by_client( 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); + if (!surface) { + continue; + } struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; struct wl_resource *resource = layer_surface->resource; From e8c421e917ac59cdf50e5fd366155ea875e4a29e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 21 Jan 2024 15:44:09 -0500 Subject: [PATCH 35/36] layer_shell: Fix typo of return instead of continue Otherwise we would skip arranging the rest of the surfaces if one of them isn't initialized. --- sway/desktop/layer_shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 968b0cdb2..c71abce7f 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -63,7 +63,7 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu } if (!surface->scene->layer_surface->initialized) { - return; + continue; } wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); From a4e85332a1fe6b079c6da1689ca33d8ba4c71977 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 22 Jan 2024 21:24:41 +0300 Subject: [PATCH 36/36] Chase wlroots!4003 --- sway/input/seatop_default.c | 5 +++-- sway/input/seatop_down.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 0f4d03850..c56330fd9 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -796,8 +796,9 @@ static void handle_pointer_axis(struct sway_seat *seat, if (!handled) { wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - roundf(scroll_factor * event->delta_discrete), event->source); + event->orientation, scroll_factor * event->delta, + roundf(scroll_factor * event->delta_discrete), event->source, + event->relative_direction); } } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 36f9bb608..b4421fe6a 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -137,7 +137,8 @@ static void handle_pointer_axis(struct sway_seat *seat, wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, event->orientation, scroll_factor * event->delta, - roundf(scroll_factor * event->delta_discrete), event->source); + roundf(scroll_factor * event->delta_discrete), event->source, + event->relative_direction); } static void handle_button(struct sway_seat *seat, uint32_t time_msec,