From d3ca83acb7dfe01df36c4f1c0a643128c1756328 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 1 Mar 2022 16:19:23 -0500 Subject: [PATCH 01/31] 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 | 24 ++++++++++++------------ sway/desktop/output.c | 12 ++++++------ sway/desktop/render.c | 16 ++++++++-------- sway/input/cursor.c | 14 +++++++------- sway/tree/output.c | 4 ++-- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index d72bf1b21..3ee4ca7ac 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -22,7 +22,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 d44d6338e..7951192cc 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -182,13 +182,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, @@ -199,13 +199,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 @@ -217,7 +217,7 @@ 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) { + &output->shell_layers[layers_above_shell[i]], link) { if (layer->layer_surface->current.keyboard_interactive && layer->layer_surface->mapped) { topmost = layer; @@ -250,7 +250,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->mapped) { @@ -298,7 +298,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; } @@ -677,7 +677,7 @@ 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); // Temporarily set the layer's current state to pending diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3f3f9494a..8962c0aef 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -334,10 +334,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, @@ -348,13 +348,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); @@ -386,7 +386,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, @@ -475,7 +475,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 ed9ad4906..4a398df78 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1138,9 +1138,9 @@ void output_render(struct sway_output *output, struct timespec *when, } render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); render_workspace(output, damage, workspace, workspace->current.focused); render_floating(output, damage); @@ -1148,14 +1148,14 @@ void output_render(struct sway_output *output, struct timespec *when, render_unmanaged(output, damage, &root->xwayland_unmanaged); #endif render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } render_seatops(output, damage); @@ -1168,9 +1168,9 @@ void output_render(struct sway_output *output, struct timespec *when, render_overlay: render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_drag_icons(output, damage, &root->drag_icons); renderer_end: diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 449aa4300..4a8efb0fb 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -99,7 +99,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; } @@ -160,22 +160,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; } @@ -186,12 +186,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 b30e646e7..5a3fd8b6e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -103,9 +103,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 a33ede4c43dfe7be89079dabfff84a99f942534d Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 4 Mar 2022 19:23:27 -0500 Subject: [PATCH 02/31] 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 0dcbf1aa6..b63936ad6 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -304,7 +304,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 8da922d50..9ce7d611c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -519,7 +519,10 @@ void handle_xdg_shell_surface(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_surface->toplevel; xdg_shell_view->map.notify = handle_map; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7c5dde530..7453c07d6 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -712,7 +712,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 0004ed141..9a24cfbe5 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -33,7 +33,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; @@ -42,6 +42,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 25da7b237d4914b17db805715f28ef6f81990fae Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 31 Aug 2022 23:42:15 -0400 Subject: [PATCH 03/31] scene_graph: Maintain `wlr_scene_node`s for the sway tree. --- include/sway/output.h | 15 +++++++++ include/sway/tree/container.h | 24 ++++++++++++++ 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 | 19 +++++++++-- sway/tree/container.c | 62 ++++++++++++++++++++++++++++++++--- sway/tree/node.c | 23 +++++++++++++ sway/tree/output.c | 45 +++++++++++++++++++++++++ sway/tree/root.c | 29 ++++++++++++++++ sway/tree/view.c | 9 +++++ sway/tree/workspace.c | 17 ++++++++++ 13 files changed, 286 insertions(+), 6 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 3ee4ca7ac..d1388695c 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "config.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -18,7 +19,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 751612e2c..271649a45 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,29 @@ 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_rect *border; + struct wlr_scene_rect *background; + + struct wlr_scene_buffer *title_buffer; + struct wlr_scene_buffer *marks_buffer; + } 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 470ee3b5e..ce3a96b6a 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -1,6 +1,7 @@ #ifndef _SWAY_NODE_H #define _SWAY_NODE_H #include +#include #include "list.h" #define MIN_SANE_W 100 @@ -74,4 +75,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 af4124a18..200f1c73a 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "sway/tree/container.h" #include "sway/tree/node.h" #include "config.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 b63936ad6..48dc53368 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 "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 b3d93a813..e64f219f0 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/tree/container.h" #include "sway/tree/node.h" @@ -22,6 +23,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 8962c0aef..cb2b3f13f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -791,6 +791,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + wlr_scene_output_destroy(output->scene_output); + output->scene_output = NULL; output->wlr_output->data = NULL; output->wlr_output = NULL; @@ -898,11 +900,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; output->damage = wlr_output_damage_create(wlr_output); wl_signal_add(&wlr_output->events.destroy, &output->destroy); diff --git a/sway/tree/container.c b/sway/tree/container.c index bf7085cbd..d75b2d16d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -30,6 +30,23 @@ #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 can and 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}); + if (!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) { @@ -37,14 +54,49 @@ 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; - if (!view) { + // 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 alloc_failure = false; + c->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); + + c->title_bar.tree = alloc_scene_tree(c->scene_node, &alloc_failure); + c->title_bar.border = alloc_rect_node(c->title_bar.node, &alloc_failure); + c->title_bar.background = alloc_rect_node(c->title_bar.node, &alloc_failure); + + c->border.tree = alloc_scene_tree(c->scene_node, &alloc_failure); + c->content_tree = alloc_scene_tree(c->border.node, &alloc_failure); + + if (view) { + // only containers with views can have borders + c->border.top = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.bottom = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.left = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.right = alloc_rect_node(c->border.tree, &alloc_failure); + } else { c->pending.children = create_list(); c->current.children = create_list(); } + + if (alloc_failure) { + wlr_scene_node_destroy(&c->scene_tree->node); + free(c); + return NULL; + } + + c->pending.layout = L_NONE; + c->view = view; + c->alpha = 1.0f; c->marks = create_list(); c->outputs = create_list(); @@ -88,6 +140,8 @@ void container_destroy(struct sway_container *con) { } } + scene_node_disown_children(con->content_node); + wlr_scene_node_destroy(con->scene_node); free(con); } diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa59..a08df5096 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -159,3 +159,26 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { } return false; } + +void scene_node_disown_children(struct wlr_scene_tree *tree) { + 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 5a3fd8b6e..2984a5a32 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -88,10 +88,53 @@ 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); + + size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *); + for (size_t i = 0; i < num_layers; i++) { + struct wlr_scene_tree *tree = + ((struct wlr_scene_tree **) &output->layers)[i]; + + if (tree) { + wlr_scene_node_destroy(&tree->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 alloc_failure = false; + size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *); + for (size_t i = 0; i < num_layers; i++) { + ((struct wlr_scene_tree **) &output->layers)[i] = + alloc_scene_tree(root->staging, &alloc_failure); + } + + if (!alloc_failure) { + output->fullscreen_background = wlr_scene_rect_create( + output->layers.fullscreen, 0, 0, (float[4]){0., 0., 0., 1.}); + + if (!output->fullscreen_background) { + sway_log(SWAY_ERROR, "Unable to allocate a background rect"); + alloc_failure = true; + } + } + + if (alloc_failure) { + 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; output->scale_filter = SCALE_FILTER_NEAREST; @@ -239,6 +282,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 7df0b2378..b7dbf2490 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" @@ -29,7 +30,34 @@ struct sway_root *root_create(void) { 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 alloc_failure = false; + root->staging = alloc_scene_tree(&root_scene->tree, &alloc_failure); + + size_t num_layers = sizeof(root->layers) / sizeof(struct wlr_scene_tree *); + for (size_t i = 0; i < num_layers; i++) { + ((struct wlr_scene_tree **) &root->layers)[i] = + alloc_scene_tree(&root_scene->tree, &alloc_failure); + } + + if (alloc_failure) { + 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_list_init(&root->all_outputs); #if HAVE_XWAYLAND @@ -51,6 +79,7 @@ void root_destroy(struct sway_root *root) { wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); list_free(root->outputs); + wlr_scene_node_destroy(&root->root_scene->tree.node); wlr_output_layout_destroy(root->output_layout); free(root); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 9a24cfbe5..ac6993ffd 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -35,6 +35,14 @@ bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { + bool alloc_failure = false; + view->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); + view->content_tree = alloc_scene_tree(view->scene_tree, &alloc_failure); + if (alloc_failure) { + wlr_scene_node_destroy(&view->scene_tree->node); + return false; + } + view->type = type; view->impl = impl; view->executed_criteria = create_list(); @@ -64,6 +72,7 @@ void view_destroy(struct sway_view *view) { } list_free(view->executed_criteria); + 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 c84320bda..c0aa651a0 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -69,6 +69,18 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); + + bool alloc_failure = false; + ws->layers.tiling = alloc_scene_tree(root->staging, &alloc_failure); + ws->layers.fullscreen = alloc_scene_tree(root->staging, &alloc_failure); + + if (alloc_failure) { + wlr_scene_node_destroy(&ws->layers.tiling->node); + wlr_scene_node_destroy(&ws->layers.fullscreen->node); + free(ws); + return NULL; + } + ws->name = name ? strdup(name) : NULL; ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); @@ -129,6 +141,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 be54b77886893d05fe1fe81507275851e99697e8 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 27 Feb 2022 17:54:53 -0500 Subject: [PATCH 04/31] scene_graph: Wire up presentation time --- sway/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/main.c b/sway/main.c index 85bc2f1c9..997a2f464 100644 --- a/sway/main.c +++ b/sway/main.c @@ -381,6 +381,8 @@ int main(int argc, char **argv) { return 1; } + wlr_scene_set_presentation(root->root_scene, server.presentation); + if (validate) { bool valid = load_main_config(config_path, false, true); free(config_path); From 8eb7ec53d4333369e491d723840ca63e1351d1f2 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 7 Aug 2022 20:13:30 -0400 Subject: [PATCH 05/31] 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 | 19 +++++++++++++++++++ sway/meson.build | 1 + sway/scene_descriptor.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 53 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..a37633cc8 --- /dev/null +++ b/include/sway/scene_descriptor.h @@ -0,0 +1,19 @@ +#ifndef _SWAY_SCENE_DESCRIPTOR_H +#define _SWAY_SCENE_DESCRIPTOR_H +#include + +enum sway_scene_descriptor_type { +}; + +struct sway_scene_descriptor { + struct wlr_scene_node *node; + enum sway_scene_descriptor_type type; + void *data; + + struct wl_listener destroy; +}; + +void scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data); + +#endif diff --git a/sway/meson.build b/sway/meson.build index 4d7dccfa8..206bd638e 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..18710dbd7 --- /dev/null +++ b/sway/scene_descriptor.c @@ -0,0 +1,33 @@ +#include +#include "log.h" +#include "sway/scene_descriptor.h" + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_scene_descriptor *desc = + wl_container_of(listener, desc, destroy); + + desc->node->data = NULL; + wl_list_remove(&desc->destroy.link); + + free(desc); +} + +void scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data) { + struct sway_scene_descriptor *desc = + calloc(1, sizeof(struct sway_scene_descriptor)); + + if (!desc) { + sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); + return; + } + + desc->type = type; + desc->data = data; + desc->node = node; + + desc->destroy.notify = handle_destroy; + wl_signal_add(&node->events.destroy, &desc->destroy); + + node->data = desc; +} From 4927531b37b7eb31b87ed6f8d3ea824cc582a131 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 1 Oct 2022 19:00:35 -0400 Subject: [PATCH 06/31] renderer: Render scene_graph --- include/sway/output.h | 1 + include/sway/scene_descriptor.h | 1 + include/sway/server.h | 2 - include/sway/surface.h | 18 -- sway/desktop/output.c | 337 +++++++++----------------------- sway/desktop/surface.c | 46 ----- sway/meson.build | 1 - sway/server.c | 3 - 8 files changed, 93 insertions(+), 316 deletions(-) delete mode 100644 include/sway/surface.h delete mode 100644 sway/desktop/surface.c diff --git a/include/sway/output.h b/include/sway/output.h index d1388695c..c399b5c37 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -61,6 +61,7 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; + struct wl_listener frame_request; struct { struct wl_signal disable; diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index a37633cc8..cf0d4a34d 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -3,6 +3,7 @@ #include enum sway_scene_descriptor_type { + SWAY_SCENE_DESC_BUFFER_TIMER, }; struct sway_scene_descriptor { diff --git a/include/sway/server.h b/include/sway/server.h index 6a5a60c8c..555074bd7 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -40,7 +40,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; @@ -157,7 +156,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 deleted file mode 100644 index fb1cd7758..000000000 --- a/include/sway/surface.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _SWAY_SURFACE_H -#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; -}; - -#endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index cb2b3f13f..21b3f3604 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -24,8 +24,8 @@ #include "sway/input/seat.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" @@ -262,104 +262,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->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 round((offset + length) * scale) - round(offset * scale); } @@ -411,173 +313,114 @@ 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_assign(struct wlr_scene_buffer *buffer) { + struct buffer_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->data) { + + } + + 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); - } else { - struct sway_surface *sway_surface = surface->data; - wl_event_source_timer_update(sway_surface->frame_done_timer, delay); - } -} + struct buffer_timer *timer = NULL; -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 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 (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { + if (buffer->node.data) { + struct sway_scene_descriptor *desc = buffer->node.data; + sway_assert(desc->type == SWAY_SCENE_DESC_BUFFER_TIMER, + "Corrupted scene tree: expected a buffer timer"); + timer = desc->data; + }else{ + timer = buffer_timer_assign(buffer); } } -#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; - } - - if (surface->buffer == NULL) { - return false; - } - - if ((float)surface->current.scale != wlr_output->scale || - surface->current.transform != wlr_output->transform) { - return false; - } - - wlr_output_attach_buffer(wlr_output, &surface->buffer->base); - if (!wlr_output_test(wlr_output)) { - return false; - } - - wlr_presentation_surface_sampled_on_output(server.presentation, surface, - wlr_output); - - return wlr_output_commit(wlr_output); + if (timer) { + wl_event_source_timer_update(timer->frame_done_timer, delay); + } else { + wlr_scene_buffer_send_frame_done(buffer, &data->when); + } } static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; - if (output->wlr_output == NULL) { - return 0; + + if (output->enabled) { + output->wlr_output->frame_pending = false; + wlr_scene_output_commit(output->scene_output); } - output->wlr_output->frame_pending = false; - - 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; - } - - 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, 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) { - return 0; - } - } - - bool needs_frame; - pixman_region32_t damage; - pixman_region32_init(&damage); - if (!wlr_output_damage_attach_render(output->damage, - &needs_frame, &damage)) { - return 0; - } - - if (needs_frame) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - output_render(output, &now, &damage); - } else { - wlr_output_rollback(output->wlr_output); - } - - pixman_region32_fini(&damage); - return 0; } -static void damage_handle_frame(struct wl_listener *listener, void *user_data) { +static void handle_frame_request(struct wl_listener *listener, void *user_data) { struct sway_output *output = - wl_container_of(listener, output, damage_frame); + wl_container_of(listener, output, frame_request); if (!output->enabled || !output->wlr_output->enabled) { return; } @@ -637,7 +480,8 @@ static void damage_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); + 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) { @@ -790,6 +634,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->commit.link); wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + wl_list_remove(&output->frame_request.link); wlr_scene_output_destroy(output->scene_output); output->scene_output = NULL; @@ -928,8 +773,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - output->damage_frame.notify = damage_handle_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame_request); + output->frame_request.notify = handle_frame_request; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 1d7b536da..000000000 --- a/sway/desktop/surface.c +++ /dev/null @@ -1,46 +0,0 @@ -#define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include "sway/server.h" -#include "sway/surface.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); - } -} diff --git a/sway/meson.build b/sway/meson.build index 206bd638e..ebf5fc628 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -19,7 +19,6 @@ sway_sources = files( 'desktop/layer_shell.c', 'desktop/output.c', 'desktop/render.c', - 'desktop/surface.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', diff --git a/sway/server.c b/sway/server.c index f67207557..79b61d796 100644 --- a/sway/server.c +++ b/sway/server.c @@ -97,9 +97,6 @@ bool server_init(struct sway_server *server) { server->compositor = wlr_compositor_create(server->wl_display, 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 a6d4f4d3b51a46cb4b79e30708592527b981a6c5 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 30 Apr 2022 00:00:25 -0400 Subject: [PATCH 07/31] scene_graph: Port seat drag icons --- include/sway/input/seat.h | 22 +++++--- 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 | 11 ---- sway/input/cursor.c | 14 +++-- sway/input/seat.c | 99 +++++++++++++++++---------------- sway/input/seatop_default.c | 26 +++++---- sway/tree/root.c | 1 - 10 files changed, 90 insertions(+), 106 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index e3a468726..a2becbde3 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/input/tablet.h" @@ -67,16 +68,12 @@ struct sway_seat_node { }; struct sway_drag_icon { - struct sway_seat *seat; struct wlr_drag_icon *wlr_drag_icon; - struct wl_list link; // sway_root::drag_icons + struct wlr_scene_tree *tree; + struct wlr_scene_tree *surface_tree; - double x, y; // in layout-local coordinates - - struct wl_listener surface_commit; - struct wl_listener map; - struct wl_listener unmap; struct wl_listener destroy; + struct wl_listener commit; }; struct sway_drag { @@ -89,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; @@ -247,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_icon_update_position(struct sway_seat *seat, struct sway_drag_icon *icon); 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 c399b5c37..e91968933 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -159,10 +159,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 cf0d4a34d..de54d133a 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -4,6 +4,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_DRAG_ICON, }; struct sway_scene_descriptor { diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 200f1c73a..f87b03259 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 21b3f3604..a3efcd70e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -246,22 +246,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->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 round((offset + length) * scale) - round(offset * scale); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 4a398df78..079f4f39f 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -201,16 +201,6 @@ static void render_unmanaged(struct sway_output *output, } #endif -static void render_drag_icons(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *drag_icons) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_drag_icons_for_each_surface(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 sway_output *output, @@ -1171,7 +1161,6 @@ render_overlay: &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_layer_popups(output, damage, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root->drag_icons); renderer_end: wlr_renderer_scissor(renderer, NULL); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 4a8efb0fb..8db513e37 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" @@ -541,11 +541,13 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { 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); - } + struct wlr_scene_node *node; + wl_list_for_each(node, &seat->drag_icons->children, link) { + struct sway_scene_descriptor *desc = node->data; + + sway_assert(desc && desc->type == SWAY_SCENE_DESC_DRAG_ICON, + "Corrupted scene tree: expected a drag icon"); + drag_icon_update_position(seat, desc->data); } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 43b207799..a723f22e9 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); @@ -365,25 +366,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->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); - +void drag_icon_update_position(struct sway_seat *seat, struct sway_drag_icon *icon) { struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; - struct sway_seat *seat = icon->seat; 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 + wlr_icon->surface->sx; - icon->y = cursor->y + wlr_icon->surface->sy; + wlr_scene_node_set_position(&icon->tree->node, cursor->x, cursor->y); break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -391,38 +382,23 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x + wlr_icon->surface->sx; - icon->y = seat->touch_y + wlr_icon->surface->sy; + wlr_scene_node_set_position(&icon->tree->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); - 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_commit(struct wl_listener *listener, void *data) { + struct sway_drag_icon *icon = wl_container_of(listener, icon, commit); + struct wlr_surface *surface = icon->wlr_drag_icon->surface; + wlr_scene_node_set_position(&icon->surface_tree->node, + surface->sx, surface->sy); } 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->commit.link); wl_list_remove(&icon->destroy.link); + wlr_scene_node_destroy(&icon->tree->node); free(icon); } @@ -500,22 +476,40 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "Allocation failed"); return; } - icon->seat = seat; + + icon->tree = wlr_scene_tree_create(seat->drag_icons); + if (!icon->tree) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); + free(icon); + return; + } + + icon->surface_tree = wlr_scene_subsurface_tree_create(icon->tree, + wlr_drag_icon->surface); + if (!icon->surface_tree) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon surface"); + wlr_scene_node_destroy(&icon->tree->node); + free(icon); + return; + } + + scene_descriptor_assign(&icon->tree->node, SWAY_SCENE_DESC_DRAG_ICON, icon); + if (!icon->tree->node.data) { + wlr_scene_node_destroy(&icon->tree->node); + free(icon); + return; + } + 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->events.unmap, &icon->unmap); - icon->map.notify = drag_icon_handle_map; - wl_signal_add(&wlr_drag_icon->events.map, &icon->map); + icon->commit.notify = drag_icon_handle_commit; + wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->commit); + icon->destroy.notify = drag_icon_handle_destroy; wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); - wl_list_insert(&root->drag_icons, &icon->link); - - drag_icon_update_position(icon); + drag_icon_update_position(seat, icon); } seatop_begin_default(seat); } @@ -562,8 +556,18 @@ struct sway_seat *seat_create(const char *seat_name) { return NULL; } + bool alloc_failure = false; + seat->scene_tree = alloc_scene_tree(root->layers.seat, &alloc_failure); + seat->drag_icons = alloc_scene_tree(seat->scene_tree, &alloc_failure); + if (alloc_failure) { + 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; } @@ -571,6 +575,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 84acefdf6..286bbeba5 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -10,6 +10,7 @@ #include "sway/input/seat.h" #include "sway/input/tablet.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" @@ -585,6 +586,17 @@ static void check_focus_follows_mouse(struct sway_seat *seat, } } +static void drag_icons_update_position(struct sway_seat *seat) { + struct wlr_scene_node *node; + wl_list_for_each(node, &seat->drag_icons->children, link) { + struct sway_scene_descriptor *desc = node->data; + + sway_assert(desc && desc->type == SWAY_SCENE_DESC_DRAG_ICON, + "Corrupted scene tree: expected drag icon"); + drag_icon_update_position(seat, desc->data); + } +} + static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_default_event *e = seat->seatop_data; struct sway_cursor *cursor = seat->cursor; @@ -608,12 +620,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; } @@ -643,12 +650,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 b7dbf2490..faa6a478e 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -63,7 +63,6 @@ struct sway_root *root_create(void) { #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 b6b6dd0d2838f862f724e6fce34baaf54226a025 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 31 May 2022 15:03:11 -0400 Subject: [PATCH 08/31] scene_graph: Port seatop_move_tiling indicators --- sway/input/seatop_move_tiling.c | 80 ++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 223c6c08c..c1abbdb8f 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,19 @@ 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 sway_output *output, pixman_region32_t *damage) { + +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) == 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, output->wlr_output->scale); - render_rect(output, damage, &box, color); + if (e->indicator_rect) { + wlr_scene_node_destroy(&e->indicator_rect->node); + e->indicator_rect = NULL; } } @@ -67,6 +57,9 @@ 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) { + if (e->indicator_rect) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, true); + } e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } @@ -165,6 +158,13 @@ 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) { + if (e->indicator_rect) { + 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; @@ -173,8 +173,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 @@ -187,8 +185,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; } @@ -201,11 +201,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) { @@ -215,6 +222,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; } @@ -256,8 +264,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; @@ -299,12 +306,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) { @@ -439,7 +442,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, @@ -451,6 +454,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, if (!e) { return; } + + const float *indicator = config->border_colors.focused.indicator; + const float color[] = { + indicator[0] * .5, + indicator[1] * .5, + indicator[2] * .5, + indicator[3] * .5 + }; + e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, + (const float *) &color); + if (e->indicator_rect) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, false); + } + e->con = con; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; @@ -468,6 +485,9 @@ void seatop_begin_move_tiling(struct sway_seat *seat, seatop_begin_move_tiling_threshold(seat, con); struct seatop_move_tiling_event *e = seat->seatop_data; if (e) { + if (e->indicator_rect) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, true); + } e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } From 74d11162945b697934d46006c8bd5372e25d2ad4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Sep 2022 12:33:00 -0400 Subject: [PATCH 09/31] scene_graph: Port ext_session_v1 --- include/sway/output.h | 1 + include/sway/server.h | 22 ++- include/sway/tree/root.h | 1 + sway/desktop/output.c | 4 + sway/desktop/render.c | 35 ---- sway/input/input-manager.c | 2 +- sway/input/keyboard.c | 2 +- sway/input/seat.c | 6 +- sway/input/switch.c | 2 +- sway/lock.c | 358 +++++++++++++++++++++++++------------ 10 files changed, 275 insertions(+), 158 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index e91968933..093b0737a 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -23,6 +23,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 555074bd7..5acea791a 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -27,6 +27,18 @@ #endif 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; @@ -91,15 +103,9 @@ struct sway_server { struct wl_listener output_manager_test; 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; @@ -161,6 +167,8 @@ 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); void handle_xdg_shell_surface(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 f87b03259..e2c43e2fe 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 a3efcd70e..69068c57c 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -765,6 +765,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 079f4f39f..cebf10553 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1046,41 +1046,6 @@ void output_render(struct sway_output *output, struct timespec *when, wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); } - if (server.session_lock.locked) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - if (server.session_lock.lock == NULL) { - // abandoned lock -> red BG - clear_color[0] = 1.f; - } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - if (server.session_lock.lock != NULL) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - - 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->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; } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 26eefc8a1..4063a8452 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -293,7 +293,7 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) struct sway_input_manager *input_manager = wl_container_of( listener, input_manager, inhibit_deactivate); struct sway_seat *seat; - if (server.session_lock.locked) { + if (server.session_lock.lock) { // Don't deactivate the grab of a screenlocker return; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 5e5692f19..08b10efa4 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -405,7 +405,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, bool exact_identifier = keyboard->wlr->group != NULL; seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); bool input_inhibited = seat->exclusive_client != NULL || - server.session_lock.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 a723f22e9..2efabf29b 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1080,7 +1080,7 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); return seat->exclusive_client == client || - (seat->exclusive_client == NULL && !server.session_lock.locked); + (seat->exclusive_client == NULL && !server.session_lock.lock); } static void send_unfocus(struct sway_container *con, void *data) { @@ -1281,8 +1281,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 fc7dfaff7..9001f0da8 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -36,7 +36,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 input_inhibited = seat->exclusive_client != NULL || - server.session_lock.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 3c7c06cf4..d2e990010 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,24 +1,35 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include "log.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "sway/output.h" #include "sway/server.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_mode; - struct wl_listener output_commit; + struct wl_listener commit; + struct wl_listener mode; + + 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) { @@ -26,99 +37,198 @@ static void set_lock_focused_surface(struct wlr_surface *focused) { } } -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); +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->mapped) { + next_focus = candidate->surface->surface; + break; + } + } + + focus_surface(output->lock, next_focus); } - 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_output_mode(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); - wlr_session_lock_surface_v1_configure(surf->lock_surface, - surf->output->width, surf->output->height); -} - -static void handle_output_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); - if (event->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); +static void handle_surface_map(struct wl_listener *listener, void *data) { + 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); } } static void handle_surface_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); + struct sway_session_lock_output *output = + wl_container_of(listener, output, surface_destroy); + refocus_output(output); - // Move the seat focus to another surface if one is available - if (server.session_lock.focused == surf->surface) { - struct wlr_surface *next_focus = NULL; + 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); +} - struct wlr_session_lock_surface_v1 *other; - wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { - if (other != surf->lock_surface && other->mapped) { - next_focus = other->surface; - break; - } - } - set_lock_focused_surface(next_focus); +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); } - - wl_list_remove(&surf->map.link); - wl_list_remove(&surf->destroy.link); - wl_list_remove(&surf->surface_commit.link); - wl_list_remove(&surf->output_mode.link); - wl_list_remove(&surf->output_commit.link); - output_damage_whole(surf->output); - free(surf); } 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_session_lock_surface *surf = calloc(1, sizeof(*surf)); - if (surf == NULL) { - return; - } + struct sway_output *output = lock_surface->output->data; sway_log(SWAY_DEBUG, "new lock layer surface"); - struct sway_output *output = lock_surface->output->data; - wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); + 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"); - surf->lock_surface = lock_surface; - surf->surface = lock_surface->surface; - surf->output = output; - surf->map.notify = handle_surface_map; - wl_signal_add(&lock_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_mode.notify = handle_output_mode; - wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode); - surf->output_commit.notify = handle_output_commit; - wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); + 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->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->mode.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_mode(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *output = + wl_container_of(listener, output, mode); + lock_output_reconfigure(output); +} + +static void lock_output_handle_commit(struct wl_listener *listener, void *data) { + struct wlr_output_event_commit *event = data; + struct sway_session_lock_output *output = + wl_container_of(listener, output, commit); + if (event->committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_TRANSFORM)) { + lock_output_reconfigure(output); + } +} + +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(struct sway_session_lock_output)); + if (!lock_output) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output"); + return NULL; + } + + 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; + } + + float *color = (float[4]){ 0.f, 0.f, 0.f, 1.f }; + if (lock->abandoned) { + color[0] = 1.f; + } + + struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, color); + 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; + } + + lock_output->output = output; + lock_output->tree = tree; + lock_output->background = background; + lock_output->lock = lock; + + 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->mode.notify = lock_output_handle_mode; + wl_signal_add(&output->wlr_output->events.mode, &lock_output->mode); + + 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) { @@ -131,33 +241,27 @@ static void handle_unlock(struct wl_listener *listener, void *data) { seat_set_focus(seat, previous); } } - - // 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); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat->exclusive_client = NULL; } - // 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) { @@ -165,44 +269,78 @@ 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(struct sway_session_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_set_exclusive_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(); + } } 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, From c8398d19c3866d6f13baad319d162a911647ff68 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 21 Feb 2022 20:57:45 -0500 Subject: [PATCH 10/31] seat: Remove dead seatop_render function --- include/sway/input/seat.h | 9 --------- sway/desktop/render.c | 10 ---------- sway/input/seat.c | 7 ------- 3 files changed, 26 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index a2becbde3..70f1f5f6d 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -43,8 +43,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 sway_output *output, - pixman_region32_t *damage); bool allow_set_cursor; }; @@ -338,13 +336,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 sway_output *output, - pixman_region32_t *damage); - bool seatop_allows_set_cursor(struct sway_seat *seat); /** diff --git a/sway/desktop/render.c b/sway/desktop/render.c index cebf10553..62fd82d86 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1006,14 +1006,6 @@ static void render_floating(struct sway_output *soutput, } } -static void render_seatops(struct sway_output *output, - pixman_region32_t *damage) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seatop_render(seat, output, damage); - } -} - void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; @@ -1113,8 +1105,6 @@ void output_render(struct sway_output *output, struct timespec *when, &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } - render_seatops(output, damage); - 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 2efabf29b..b1be09085 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1684,13 +1684,6 @@ void seatop_end(struct sway_seat *seat) { seat->seatop_impl = NULL; } -void seatop_render(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage) { - if (seat->seatop_impl->render) { - seat->seatop_impl->render(seat, output, damage); - } -} - bool seatop_allows_set_cursor(struct sway_seat *seat) { return seat->seatop_impl->allow_set_cursor; } From fdc2053bfe4874c4f7da98d17a07657880079a17 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 21 Feb 2022 15:21:52 -0500 Subject: [PATCH 11/31] renderer: Remove in favor of scene_graph --- include/sway/output.h | 6 - sway/desktop/render.c | 1148 ----------------------------------------- sway/meson.build | 1 - 3 files changed, 1155 deletions(-) delete mode 100644 sway/desktop/render.c diff --git a/include/sway/output.h b/include/sway/output.h index 093b0737a..cfeee3055 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -177,12 +177,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 sway_output *output, - pixman_region32_t *output_damage, 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 62fd82d86..000000000 --- a/sway/desktop/render.c +++ /dev/null @@ -1,1148 +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 { - pixman_region32_t *damage; - float alpha; - struct wlr_box *clip_box; -}; - -/** - * 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 round((offset + length) * scale) - round(offset * scale); -} - -static void scissor_output(struct wlr_output *wlr_output, - pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_output->renderer; - assert(renderer); - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(wlr_output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - wlr_renderer_scissor(renderer, &box); -} - -static void set_scale_filter(struct wlr_output *wlr_output, - struct wlr_texture *texture, enum scale_filter_mode scale_filter) { - if (!wlr_texture_is_gles2(texture)) { - return; - } - - struct wlr_gles2_texture_attribs attribs; - wlr_gles2_texture_get_attribs(texture, &attribs); - - glBindTexture(attribs.target, attribs.tex); - - switch (scale_filter) { - case SCALE_FILTER_LINEAR: - glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; - case SCALE_FILTER_NEAREST: - glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - assert(false); // unreachable - } -} - -static void render_texture(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9], float alpha) { - struct wlr_renderer *renderer = wlr_output->renderer; - struct sway_output *output = wlr_output->data; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, - dst_box->width, dst_box->height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - set_scale_filter(wlr_output, texture, output->scale_filter); - if (src_box != NULL) { - wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); - } else { - wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); - } - } - -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; - pixman_region32_t *output_damage = data->damage; - 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 proj_box = *_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, - wlr_output->transform_matrix); - - struct wlr_box dst_box = *_box; - struct wlr_box *clip_box = data->clip_box; - if (clip_box != NULL) { - dst_box.width = fmin(dst_box.width, clip_box->width); - dst_box.height = fmin(dst_box.height, clip_box->height); - } - scale_box(&dst_box, wlr_output->scale); - - render_texture(wlr_output, output_damage, texture, - &src_box, &dst_box, matrix, alpha); - - wlr_presentation_surface_sampled_on_output(server.presentation, surface, - wlr_output); -} - -static void render_layer_toplevel(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_layer_for_each_toplevel_surface(output, layer_surfaces, - render_surface_iterator, &data); -} - -static void render_layer_popups(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_layer_for_each_popup_surface(output, layer_surfaces, - render_surface_iterator, &data); -} - -#if HAVE_XWAYLAND -static void render_unmanaged(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *unmanaged) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_unmanaged_for_each_surface(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 sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4]) { - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_renderer *renderer = wlr_output->renderer; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_render_rect(renderer, &box, color, - wlr_output->transform_matrix); - } - -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 sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .damage = damage, - .alpha = alpha, - }; - 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 - - output->lx - view->geometry.x; - double oy = view->container->surface_y - - output->ly - view->geometry.y; - output_surface_for_each_surface(output, view->surface, ox, oy, - render_surface_iterator, &data); -} - -static void render_view_popups(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .damage = damage, - .alpha = alpha, - }; - output_view_for_each_popup_surface(output, view, - render_surface_iterator, &data); -} - -static void render_saved_view(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - 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; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0, - wlr_output->transform_matrix); - - if (!floating) { - dst_box.width = fmin(dst_box.width, - view->container->current.content_width - - (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); - dst_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); - - render_texture(wlr_output, damage, saved_buf->buffer->texture, - &saved_buf->source_box, &dst_box, matrix, 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 sway_output *output, pixman_region32_t *damage, - struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->view; - if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(view, output, damage, view->container->alpha); - } else if (view->surface) { - render_view_toplevels(view, output, damage, view->container->alpha); - } - - if (con->current.border == B_NONE || con->current.border == B_CSD) { - return; - } - - struct wlr_box box; - float output_scale = 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(output, damage, &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(output, damage, &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(output, damage, &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 sway_output *output, - pixman_region32_t *output_damage, 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]; - 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(output, output_damage, &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(output, output_damage, &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(output, output_damage, &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(output, output_damage, &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 = round(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; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width < texture_box.width) { - texture_box.width = ob_inner_width; - } - render_texture(output->wlr_output, output_damage, marks_texture, - NULL, &texture_box, matrix, con->alpha); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; - box.height = ob_padding_above; - render_rect(output, output_damage, &box, color); - - // Padding below - box.y += ob_padding_above + texture_box.height; - box.height = ob_padding_below; - render_rect(output, output_damage, &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 = round((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; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width - ob_marks_width < texture_box.width) { - texture_box.width = ob_inner_width - ob_marks_width; - } - - render_texture(output->wlr_output, output_damage, title_texture, - NULL, &texture_box, matrix, con->alpha); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; - box.height = ob_padding_above; - render_rect(output, output_damage, &box, color); - - // Padding below - box.y += ob_padding_above + texture_box.height; - box.height = ob_padding_below; - render_rect(output, output_damage, &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 = round(bg_y * output_scale); - box.height = ob_bg_height; - render_rect(output, output_damage, &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(output, output_damage, &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(output, output_damage, &box, color); -} - -/** - * Render the top border line for a view using "border pixel". - */ -static void render_top_border(struct sway_output *output, - pixman_region32_t *output_damage, 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 = 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(output, output_damage, &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 sway_output *output, - pixman_region32_t *damage, 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 sway_output *output, - pixman_region32_t *damage, 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(output, damage, child, floor(state->x), - floor(state->y), state->width, colors, - title_texture, marks_texture); - } else if (state->border == B_PIXEL) { - render_top_border(output, damage, child, colors); - } - render_view(output, damage, child, colors); - } else { - render_container(output, damage, 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 sway_output *output, - pixman_region32_t *damage, 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(output, damage, 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(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent->focused || current->current.focused); - } -} - -/** - * Render a container's children using the L_STACKED layout. - */ -static void render_containers_stacked(struct sway_output *output, - pixman_region32_t *damage, 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(output, damage, 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(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent->focused || current->current.focused); - } -} - -static void render_containers(struct sway_output *output, - pixman_region32_t *damage, 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(output,damage, parent); - return; - } - } - - switch (parent->layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_containers_linear(output, damage, parent); - break; - case L_STACKED: - render_containers_stacked(output, damage, parent); - break; - case L_TABBED: - render_containers_tabbed(output, damage, parent); - break; - } -} - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, 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(output, damage, &data); -} - -static void render_workspace(struct sway_output *output, - pixman_region32_t *damage, 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(output, damage, &data); -} - -static void render_floating_container(struct sway_output *soutput, - pixman_region32_t *damage, 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(soutput, damage, 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(soutput, damage, con, colors); - } - render_view(soutput, damage, con, colors); - } else { - render_container(soutput, damage, con, con->current.focused); - } -} - -static void render_floating(struct sway_output *soutput, - pixman_region32_t *damage) { - 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(soutput, damage, floater); - } - } - } -} - -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_renderer *renderer = output->server->renderer; - - 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; - } - - wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); - - 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); - } - - if (!pixman_region32_not_empty(damage)) { - // Output isn't damaged but needs buffer swap - goto renderer_end; - } - - if (debug.damage == DAMAGE_HIGHLIGHT) { - wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); - } - - if (output_has_opaque_overlay_layer_surface(output)) { - goto render_overlay; - } - - if (fullscreen_con) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - if (fullscreen_con->view) { - if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { - render_saved_view(fullscreen_con->view, output, damage, 1.0f); - } else if (fullscreen_con->view->surface) { - render_view_toplevels(fullscreen_con->view, - output, damage, 1.0f); - } - } else { - render_container(output, damage, 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(output, damage, floater); - } - } -#if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); -#endif - } else { - float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - render_layer_toplevel(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - render_workspace(output, damage, workspace, workspace->current.focused); - render_floating(output, damage); -#if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); -#endif - render_layer_toplevel(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - - render_layer_popups(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(output, damage, - &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(focus->view, output, damage, focus->alpha); - } - -render_overlay: - render_layer_toplevel(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(output, damage, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - -renderer_end: - wlr_renderer_scissor(renderer, NULL); - wlr_output_render_software_cursors(wlr_output, damage); - wlr_renderer_end(renderer); - - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, &output->damage->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_output_set_damage(wlr_output, &frame_damage); - pixman_region32_fini(&frame_damage); - - if (!wlr_output_commit(wlr_output)) { - return; - } - output->last_frame = *when; -} diff --git a/sway/meson.build b/sway/meson.build index ebf5fc628..85f82aba4 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/transaction.c', 'desktop/xdg_shell.c', From f9fc1ff7a8423d8dfc5e8ff0981080ec53550e30 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 17 Oct 2022 15:26:27 -0400 Subject: [PATCH 12/31] Introduce sway_text_buffer This is a helper on top of a wlr_scene_buffer that will handle text rendering for us. --- include/sway/sway_text_buffer.h | 25 +++ sway/meson.build | 1 + sway/sway_text_buffer.c | 340 ++++++++++++++++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 include/sway/sway_text_buffer.h create mode 100644 sway/sway_text_buffer.c diff --git a/include/sway/sway_text_buffer.h b/include/sway/sway_text_buffer.h new file mode 100644 index 000000000..5248defc1 --- /dev/null +++ b/include/sway/sway_text_buffer.h @@ -0,0 +1,25 @@ +#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]; + + struct wlr_scene_node *node; +}; + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, const float *color, bool pango_markup); + +void sway_text_node_set_color(struct sway_text_node *node, const float *color); + +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); + +#endif diff --git a/sway/meson.build b/sway/meson.build index 85f82aba4..656d4bd50 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_buffer.c', 'swaynag.c', 'xdg_activation_v1.c', 'xdg_decoration.c', diff --git a/sway/sway_text_buffer.c b/sway/sway_text_buffer.c new file mode 100644 index 000000000..85ee4233c --- /dev/null +++ b/sway/sway_text_buffer.c @@ -0,0 +1,340 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include "cairo_util.h" +#include "pango.h" +#include "sway/sway_text_buffer.h" +#include "sway/config.h" +#include "log.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; + + float scale; + enum wl_output_subpixel subpixel; + + struct wl_list outputs; // text_buffer_output.link + + struct wl_listener output_enter; + struct wl_listener output_leave; + struct wl_listener destroy; +}; + +struct text_buffer_output { + struct wl_list link; + struct wlr_output *output; + struct text_buffer *text_buffer; + + struct wl_listener commit; +}; + +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) { + float scale = buffer->scale; + int width = ceil(buffer->props.width * scale); + int height = ceil(buffer->props.height * scale); + float *color = (float *) &buffer->props.color; + 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; + } + + cairo_t *cairo = cairo_create(surface); + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_set_font_options(cairo, fo); + pango = pango_cairo_create_context(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); + + struct cairo_buffer *cairo_buffer = calloc(1, sizeof(struct cairo_buffer)); + 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); + +err: + if (pango) g_object_unref(pango); + cairo_font_options_destroy(fo); +} + +static void ensure_backing_buffer(struct text_buffer *buffer) { + float scale = 0; + enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + + struct text_buffer_output *output; + wl_list_for_each(output, &buffer->outputs, link) { + 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; + } + } + + // no outputs + if (scale == 0) { + return; + } + + if (scale != buffer->scale || subpixel != buffer->subpixel) { + buffer->scale = scale; + buffer->subpixel = subpixel; + render_backing_buffer(buffer); + } +} + +static void handle_output_commit(struct wl_listener *listener, void *data) { + struct text_buffer_output *output = wl_container_of(listener, output, commit); + struct wlr_output_event_commit *event = data; + + if (event->committed & (WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_SUBPIXEL)) { + ensure_backing_buffer(output->text_buffer); + } +} + +static void handle_output_enter(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, output_enter); + struct wlr_scene_output *output = data; + struct text_buffer_output *buffer_output = + calloc(1, sizeof(struct text_buffer_output)); + if (!buffer_output) { + return; + } + + buffer_output->text_buffer = buffer; + + buffer_output->commit.notify = handle_output_commit; + wl_signal_add(&output->output->events.commit, &buffer_output->commit); + + buffer_output->output = output->output; + wl_list_insert(&buffer->outputs, &buffer_output->link); + ensure_backing_buffer(buffer); +} + +static void text_buffer_output_destroy(struct text_buffer_output *output) { + if (!output) { + return; + } + + wl_list_remove(&output->link); + wl_list_remove(&output->commit.link); + free(output); +} + +static struct text_buffer_output *get_text_output_from_wlr_output( + struct text_buffer *buffer, struct wlr_output *wlr_output) { + struct text_buffer_output *output; + wl_list_for_each(output, &buffer->outputs, link) { + if (output->output == wlr_output) { + return output; + } + } + return NULL; +} + +static void handle_output_leave(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, output_leave); + struct wlr_scene_output *scene_output = data; + + struct text_buffer_output *output = get_text_output_from_wlr_output( + buffer, scene_output->output); + text_buffer_output_destroy(output); + ensure_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->output_enter.link); + wl_list_remove(&buffer->output_leave.link); + wl_list_remove(&buffer->destroy.link); + + struct text_buffer_output *output, *tmp_output; + wl_list_for_each_safe(output, tmp_output, &buffer->outputs, link) { + text_buffer_output_destroy(output); + } + + 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); + 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, const float *color, bool pango_markup) { + struct text_buffer *buffer = calloc(1, sizeof(struct text_buffer)); + if (buffer == NULL) { + 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; + } + + wl_list_init(&buffer->outputs); + + buffer->props.height = config->font_height; + buffer->props.pango_markup = pango_markup; + memcpy(&buffer->props.color, color, sizeof(float) * 4); + + buffer->destroy.notify = handle_destroy; + wl_signal_add(&node->node.events.destroy, &buffer->destroy); + buffer->output_enter.notify = handle_output_enter; + wl_signal_add(&node->events.output_enter, &buffer->output_enter); + buffer->output_leave.notify = handle_output_leave; + wl_signal_add(&node->events.output_leave, &buffer->output_leave); + + text_calc_size(buffer); + + return &buffer->props; +} + +void sway_text_node_set_color(struct sway_text_node *node, const float *color) { + if (memcmp(&node->color, color, sizeof(float) * 4) == 0) { + return; + } + + memcpy(&node->color, color, sizeof(float) * 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); +} From 4b39a8a84732cf9f951830d2860c3c826adca032 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 31 May 2022 15:11:17 -0400 Subject: [PATCH 13/31] input: Query scene graph for relevant surface/node intersections --- include/sway/scene_descriptor.h | 2 + sway/input/cursor.c | 184 ++++++++------------------------ sway/tree/container.c | 16 +++ sway/tree/root.c | 7 ++ 4 files changed, 72 insertions(+), 137 deletions(-) diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index de54d133a..e69d3f67b 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -4,6 +4,8 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_NON_INTERACTIVE, + SWAY_SCENE_DESC_CONTAINER, SWAY_SCENE_DESC_DRAG_ICON, }; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8db513e37..b389fbbea 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -37,165 +37,75 @@ 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) { - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; - } - return false; -} - -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). */ 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_surface **_surface, double *sx, double *sy) { + struct wlr_scene_node *scene_node = NULL; + size_t num_layers = sizeof(root->layers) / sizeof(struct wlr_scene_tree *); + for (int i = num_layers - 1; i >= 0 && !scene_node; i--) { + struct wlr_scene_tree *layer = ((struct wlr_scene_tree **) &root->layers)[i]; + struct sway_scene_descriptor *desc = layer->node.data; + + if (!desc || desc->type != SWAY_SCENE_DESC_NON_INTERACTIVE) { + scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); + } + } + + 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_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) { + if (current->data) { + struct sway_scene_descriptor *desc = current->data; + + if (desc->type == SWAY_SCENE_DESC_CONTAINER) { + struct sway_container *con = desc->data; + 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 d75b2d16d..65a5c9b38 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -20,6 +20,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/tree/arrange.h" @@ -88,6 +89,21 @@ struct sway_container *container_create(struct sway_view *view) { c->current.children = create_list(); } + scene_descriptor_assign(c->scene_node, SWAY_SCENE_DESC_CONTAINER, c); + if (!c->scene_node->data) { + alloc_failure = true; + } + + // also assign the scene descriptor to the view. On fullscreen clients + // the container will not be part of the scene graph, only the view itself + // So to not confuse input logic, we need to assign this. + if (view) { + scene_descriptor_assign(view->scene_node, SWAY_SCENE_DESC_CONTAINER, c); + if (!view->scene_node->data) { + alloc_failure = true; + } + } + if (alloc_failure) { wlr_scene_node_destroy(&c->scene_tree->node); free(c); diff --git a/sway/tree/root.c b/sway/tree/root.c index faa6a478e..666d5103c 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -8,6 +8,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" @@ -50,6 +51,12 @@ struct sway_root *root_create(void) { alloc_scene_tree(&root_scene->tree, &alloc_failure); } + scene_descriptor_assign(&root->layers.seat->node, + SWAY_SCENE_DESC_NON_INTERACTIVE, NULL); + if (!root->layers.seat->node.data) { + alloc_failure = true; + } + if (alloc_failure) { wlr_scene_node_destroy(&root_scene->tree.node); free(root); From e45e5834340e2f600fde3c037d64b4c6c61d1b12 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 8 Aug 2022 17:42:10 -0400 Subject: [PATCH 14/31] 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 | 698 +++++++++----------------------- sway/desktop/output.c | 85 ---- sway/input/cursor.c | 4 + sway/tree/output.c | 5 - 8 files changed, 222 insertions(+), 639 deletions(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index f85084934..0cc1ea0a1 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -4,60 +4,32 @@ #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 *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; -}; - struct sway_output; 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 cfeee3055..3411f0247 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -21,8 +21,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; @@ -38,7 +42,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 timespec last_frame; @@ -123,8 +126,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_render(struct sway_output *output, struct timespec *when, @@ -142,18 +143,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 e69d3f67b..3f96338aa 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -6,6 +6,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_BUFFER_TIMER, SWAY_SCENE_DESC_NON_INTERACTIVE, SWAY_SCENE_DESC_CONTAINER, + 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 e2c43e2fe..8e3d45d2d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -36,10 +36,15 @@ struct sway_root { struct wlr_scene_tree *staging; 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 7951192cc..68659b150 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -7,6 +7,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,163 +17,25 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" +#include -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_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; - struct wlr_layer_surface_v1_state *state = &layer->current; - if (exclusive != (state->exclusive_zone > 0)) { +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_scene_descriptor *desc = node->data; + + // the descriptor could be null during the node's destruction + if (!desc) { 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); + + sway_assert(desc->type == SWAY_SCENE_DESC_LAYER_SHELL, + "Corrupted scene tree: expected a layer shell node"); + + struct sway_layer_surface *surface = desc->data; + wlr_scene_layer_surface_v1_configure(surface->scene, + full_area, usable_area); } } @@ -180,16 +43,12 @@ 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) { @@ -197,64 +56,64 @@ void arrange_layers(struct sway_output *output) { memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 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->mapped) { - topmost = layer; - break; - } - } - if (topmost != NULL) { - break; - } +static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, + enum zwlr_layer_shell_v1_layer type) { + switch (type) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + return output->layers.shell_background; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + return output->layers.shell_bottom; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + return output->layers.shell_top; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + return output->layers.shell_overlay; } - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (topmost != NULL) { - seat_set_focus_layer(seat, topmost->layer_surface); - } else if (seat->focused_layer && - !seat->focused_layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, NULL); - } + sway_assert(false, "unreachable"); + return NULL; +} + +static struct sway_layer_surface *sway_layer_surface_create( + struct wlr_scene_layer_surface_v1 *scene) { + struct sway_layer_surface *surface = calloc(1, sizeof(struct sway_layer_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->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_scene_descriptor *desc = node->data; + struct sway_layer_surface *surface = desc->data; + 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->mapped) { - return lsurface; + && layer_surface->mapped) { + return surface; } } } @@ -262,293 +121,131 @@ 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); + // 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); + wl_resource_get_client(layer->layer_surface->resource); bool set_focus = seat->exclusive_client == client; - if (set_focus) { - 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); + 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 wlr_box old_extent = layer->extent; + struct sway_layer_surface *surface = + wl_container_of(listener, surface, surface_commit); - bool layer_changed = false; - if (layer_surface->current.committed != 0 - || layer->mapped != layer_surface->mapped) { - layer->mapped = layer_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); + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + 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) { - 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 (committed || layer_surface->mapped != surface->mapped) { + surface->mapped = layer_surface->mapped; + arrange_layers(surface->output); + transaction_commit_dirty(); } - transaction_commit_dirty(); + int lx, ly; + wlr_scene_node_coords(&surface->scene->tree->node, &lx, &ly); + wlr_scene_node_set_position(&surface->popups->node, lx, ly); } -static void unmap(struct sway_layer_surface *sway_layer) { +static void handle_map(struct wl_listener *listener, void *data) { + struct sway_layer_surface *surface = wl_container_of(listener, + surface, map); + + struct wlr_layer_surface_v1 *layer_surface = + surface->scene->layer_surface; + + // focus on new surface + if (layer_surface->current.keyboard_interactive && + (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || + layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + // but only if the currently focused layer has a lower precedence + if (!seat->focused_layer || + seat->focused_layer->current.layer >= layer_surface->current.layer) { + seat_set_focus_layer(seat, layer_surface); + } + } + arrange_layers(surface->output); + } + + cursor_rebase_all(); +} + +static void handle_unmap(struct wl_listener *listener, void *data) { + 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 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); - if (sway_layer->layer_surface->mapped) { - unmap(sway_layer); - } - - 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); -} - -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); - wlr_surface_send_enter(sway_layer->layer_surface->surface, - sway_layer->layer_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); - unmap(sway_layer); -} - -static void subsurface_damage(struct sway_layer_subsurface *subsurface, - bool whole) { - struct sway_layer_surface *layer = subsurface->layer_surface; - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; - int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; - output_damage_surface( - output, ox, oy, subsurface->wlr_subsurface->surface, whole); -} - -static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, unmap); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, map); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, commit); - subsurface_damage(subsurface, false); -} - -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { - wl_list_remove(&subsurface->link); - wl_list_remove(&subsurface->map.link); - wl_list_remove(&subsurface->unmap.link); - wl_list_remove(&subsurface->destroy.link); - wl_list_remove(&subsurface->commit.link); - free(subsurface); -} - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - layer_subsurface_destroy(subsurface); -} - -static struct sway_layer_subsurface *create_subsurface( - struct wlr_subsurface *wlr_subsurface, - struct sway_layer_surface *layer_surface) { - struct sway_layer_subsurface *subsurface = - calloc(1, sizeof(struct sway_layer_subsurface)); - if (subsurface == NULL) { - return NULL; - } - - subsurface->wlr_subsurface = wlr_subsurface; - subsurface->layer_surface = layer_surface; - wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); - - subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); - subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->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); -} - -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"); - wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); - 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); - 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); + wl_list_remove(&popup->new_popup.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; + 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, }; @@ -559,25 +256,25 @@ static void popup_unconstrain(struct sway_layer_popup *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_surface *toplevel, struct wlr_scene_tree *parent) { struct sway_layer_popup *popup = calloc(1, sizeof(struct sway_layer_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->events.map, &popup->map); - popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->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); @@ -590,19 +287,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) { @@ -634,10 +326,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; } @@ -646,39 +334,53 @@ 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; + } + + scene_descriptor_assign(&scene_surface->tree->node, + SWAY_SCENE_DESC_LAYER_SHELL, surface); + if (!scene_surface->tree->node.data) { + // 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->events.map, &surface->map); + surface->unmap.notify = handle_unmap; + wl_signal_add(&layer_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->events.map, &sway_layer->map); - sway_layer->unmap.notify = handle_unmap; - wl_signal_add(&layer_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); // Temporarily set the layer's current state to pending // So that we can easily arrange it diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 69068c57c..62ac30e02 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -169,66 +169,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, @@ -269,31 +209,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 b389fbbea..4cbec3bc6 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -77,6 +77,10 @@ struct sway_node *node_at_coords( if (desc->type == SWAY_SCENE_DESC_CONTAINER) { struct sway_container *con = desc->data; return &con->node; + } else if (desc->type == SWAY_SCENE_DESC_LAYER_SHELL) { + // We don't want to feed through the current workspace on + // layer shells + return NULL; } } diff --git a/sway/tree/output.c b/sway/tree/output.c index 2984a5a32..ad7abcfdf 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -146,11 +146,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; } From 84168eb70c4310491f1c7bcc968457ea0c692b9b Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 21 Feb 2022 16:07:17 -0500 Subject: [PATCH 15/31] container: Remove dead location query code --- include/sway/tree/container.h | 13 -- sway/tree/container.c | 259 ---------------------------------- 2 files changed, 272 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 271649a45..5f94ad7ea 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -170,19 +170,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/sway/tree/container.c b/sway/tree/container.c index 65a5c9b38..01b510a83 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -244,265 +244,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_surface_is_xdg_surface(surface)) { - if (!wlr_surface_is_subsurface(surface)) { - return false; - } - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - surface = subsurface->parent; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; -} - -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) { From 425607608d4ff81cb7813a52f5c68125c891d948 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 1 Nov 2022 14:09:47 -0400 Subject: [PATCH 16/31] scene_graph: Port container server side decorations --- include/sway/tree/container.h | 38 +-- include/sway/tree/root.h | 1 - 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/desktop/output.c | 1 - sway/tree/container.c | 479 +++++++++++++++++++--------------- sway/tree/view.c | 10 +- 11 files changed, 306 insertions(+), 262 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 5f94ad7ea..db188fea4 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -77,8 +77,8 @@ struct sway_container { struct wlr_scene_rect *border; struct wlr_scene_rect *background; - struct wlr_scene_buffer *title_buffer; - struct wlr_scene_buffer *marks_buffer; + struct sway_text_node *title_text; + struct sway_text_node *marks_text; } title_bar; struct { @@ -97,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; @@ -139,18 +140,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; @@ -192,7 +182,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); @@ -225,11 +217,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. */ @@ -303,15 +290,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, @@ -359,8 +341,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); @@ -384,4 +364,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/include/sway/tree/root.h b/include/sway/tree/root.h index 8e3d45d2d..389bdad09 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "sway/tree/container.h" #include "sway/tree/node.h" diff --git a/sway/commands/client.c b/sway/commands/client.c index 772631456..43a573093 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 title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_title_bar(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(title_bar_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/desktop/output.c b/sway/desktop/output.c index 62ac30e02..4fd1d2c2d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -572,7 +572,6 @@ static void handle_mode(struct wl_listener *listener, void *data) { static void update_textures(struct sway_container *con, void *data) { container_update_title_textures(con); - container_update_marks_textures(con); } static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 01b510a83..da4163a98 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -5,22 +5,23 @@ #include #include #include +#include +#include #include +#include #include #include #include #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_buffer.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -71,12 +72,12 @@ struct sway_container *container_create(struct sway_view *view) { bool alloc_failure = false; c->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); - c->title_bar.tree = alloc_scene_tree(c->scene_node, &alloc_failure); - c->title_bar.border = alloc_rect_node(c->title_bar.node, &alloc_failure); - c->title_bar.background = alloc_rect_node(c->title_bar.node, &alloc_failure); + c->title_bar.tree = alloc_scene_tree(c->scene_tree, &alloc_failure); + c->title_bar.border = alloc_rect_node(c->title_bar.tree, &alloc_failure); + c->title_bar.background = alloc_rect_node(c->title_bar.tree, &alloc_failure); - c->border.tree = alloc_scene_tree(c->scene_node, &alloc_failure); - c->content_tree = alloc_scene_tree(c->border.node, &alloc_failure); + c->border.tree = alloc_scene_tree(c->scene_tree, &alloc_failure); + c->content_tree = alloc_scene_tree(c->border.tree, &alloc_failure); if (view) { // only containers with views can have borders @@ -89,8 +90,8 @@ struct sway_container *container_create(struct sway_view *view) { c->current.children = create_list(); } - scene_descriptor_assign(c->scene_node, SWAY_SCENE_DESC_CONTAINER, c); - if (!c->scene_node->data) { + scene_descriptor_assign(&c->scene_tree->node, SWAY_SCENE_DESC_CONTAINER, c); + if (!c->scene_tree->node.data) { alloc_failure = true; } @@ -98,8 +99,8 @@ struct sway_container *container_create(struct sway_view *view) { // the container will not be part of the scene graph, only the view itself // So to not confuse input logic, we need to assign this. if (view) { - scene_descriptor_assign(view->scene_node, SWAY_SCENE_DESC_CONTAINER, c); - if (!view->scene_node->data) { + scene_descriptor_assign(&view->scene_tree->node, SWAY_SCENE_DESC_CONTAINER, c); + if (!view->scene_tree->node.data) { alloc_failure = true; } } @@ -119,9 +120,244 @@ struct sway_container *container_create(struct sway_view *view) { wl_signal_init(&c->events.destroy); wl_signal_emit(&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; +} + +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; + + 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; + } + + const float *bottom, *right; + bottom = (const float *) colors->child_border; + right = (const float *) colors->child_border; + + if (!container_is_current_floating(con) && siblings && siblings->length == 1) { + if (layout == L_HORIZ) { + right = (const float *) colors->indicator; + } else if (layout == L_VERT) { + bottom = (const float *) colors->indicator; + } + } + + wlr_scene_rect_set_color(con->title_bar.border, (const float *) colors->border); + wlr_scene_rect_set_color(con->title_bar.background, (const float *) colors->background); + + if (con->view) { + wlr_scene_rect_set_color(con->border.top, (const float *) colors->child_border); + wlr_scene_rect_set_color(con->border.bottom, bottom); + wlr_scene_rect_set_color(con->border.left, (const float *) colors->child_border); + wlr_scene_rect_set_color(con->border.right, right); + } + + if (con->title_bar.title_text) { + sway_text_node_set_color(con->title_bar.title_text, colors->text); + } + + if (con->title_bar.marks_text) { + sway_text_node_set_color(con->title_bar.marks_text, colors->text); + } +} + +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); + } +} + +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(); + + 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); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + } + + 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); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + } +} + +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")) { @@ -133,21 +369,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; @@ -156,8 +382,8 @@ void container_destroy(struct sway_container *con) { } } - scene_node_disown_children(con->content_node); - wlr_scene_node_destroy(con->scene_node); + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); free(con); } @@ -306,110 +532,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(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); - 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 +597,12 @@ 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); + } else { + container_update_title_bar(con); + } } if (con->pending.parent) { container_update_representation(con->pending.parent); @@ -746,17 +873,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; @@ -1192,7 +1308,6 @@ void container_discover_outputs(struct sway_container *con) { 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); } } @@ -1206,14 +1321,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; @@ -1231,13 +1338,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) { @@ -1452,7 +1552,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; } @@ -1483,70 +1583,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 ac6993ffd..8185666cc 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -24,6 +24,7 @@ #include "sway/output.h" #include "sway/input/seat.h" #include "sway/server.h" +#include "sway/sway_text_buffer.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -1310,7 +1311,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"); @@ -1377,6 +1384,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 8d6080bd5a0e39c72a5ef3f9b2a1e2a55a815212 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 4 Mar 2022 20:38:04 -0500 Subject: [PATCH 17/31] 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 f5a3a053f..fed25a881 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 c3615a8b57a608db80d70f794229a1f271f1ad15 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 31 Aug 2022 21:44:19 -0400 Subject: [PATCH 18/31] scene_graph: Port xdg_shell --- include/sway/scene_descriptor.h | 1 + include/sway/tree/view.h | 18 +-- sway/desktop/output.c | 15 +- sway/desktop/xdg_shell.c | 63 +++++---- sway/input/cursor.c | 4 + sway/tree/view.c | 244 -------------------------------- 6 files changed, 58 insertions(+), 287 deletions(-) diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 3f96338aa..746ae7d8b 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -7,6 +7,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_NON_INTERACTIVE, SWAY_SCENE_DESC_CONTAINER, 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 48dc53368..f1c1ee087 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -122,8 +122,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; @@ -188,7 +186,6 @@ struct sway_xwayland_unmanaged { 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); }; @@ -203,20 +200,9 @@ struct sway_view_child { 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 wlr_scene_tree *scene_tree; + struct wlr_scene_tree *xdg_surface_tree; }; struct sway_xdg_popup { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 4fd1d2c2d..0932e5b97 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -273,7 +273,20 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, while (true) { if (current->data) { - + struct sway_scene_descriptor *desc = current->data; + + if (desc->type == SWAY_SCENE_DESC_CONTAINER) { + struct sway_container *con = desc->data; + + // We can be dealing with a title bar without owned by a + // container without a view. This will happen if you have a + // nested tabbed view for example. + if (con->view) { + view_max_render_time = con->view->max_render_time; + } + + break; + } } if (!current->parent) { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 9ce7d611c..faac1ec72 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" @@ -21,17 +21,6 @@ static const struct sway_view_child_impl popup_impl; -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_destroy(struct sway_view_child *child) { if (!sway_assert(child->impl == &popup_impl, "Expected an xdg_shell popup")) { @@ -40,22 +29,25 @@ static void popup_destroy(struct sway_view_child *child) { struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->destroy.link); + + wlr_scene_node_destroy(&child->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); + struct wlr_xdg_popup *wlr_popup, struct sway_view *view, + struct wlr_scene_tree *parent); 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); + popup_create(wlr_popup, popup->child.view, popup->child.xdg_surface_tree); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { @@ -82,7 +74,8 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { } static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { + 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 = @@ -90,19 +83,30 @@ static struct sway_xdg_popup *popup_create( if (popup == NULL) { return NULL; } + + struct wlr_scene_tree *scene_tree = wlr_scene_tree_create(parent); + if (!scene_tree) { + free(popup); + return NULL; + } + + scene_descriptor_assign(&scene_tree->node, SWAY_SCENE_DESC_POPUP, popup); + view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 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; + + popup->child.scene_tree = scene_tree; + popup->child.xdg_surface_tree = + wlr_scene_xdg_surface_create(scene_tree, xdg_surface); wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); - wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); - popup_unconstrain(popup); - return popup; } @@ -284,7 +288,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); @@ -292,15 +295,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) { @@ -322,7 +322,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->child.view->content_tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->child.scene_tree->node, lx, ly); } static void handle_request_maximize(struct wl_listener *listener, void *data) { @@ -523,7 +532,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { free(xdg_shell_view); return; } - xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; xdg_shell_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); @@ -534,5 +542,8 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { xdg_shell_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); + xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; + wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_surface); + xdg_surface->data = xdg_shell_view; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 4cbec3bc6..2ec284267 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -81,6 +81,10 @@ struct sway_node *node_at_coords( // We don't want to feed through the current workspace on // layer shells return NULL; + } else if (desc->type == SWAY_SCENE_DESC_POPUP) { + struct sway_xdg_popup *popup = desc->data; + struct sway_container *con = popup->child.view->container; + return &con->node; } } diff --git a/sway/tree/view.c b/sway/tree/view.c index 8185666cc..459d71440 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -472,24 +472,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) { @@ -788,11 +770,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); } @@ -860,8 +837,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, void view_unmap(struct sway_view *view) { wl_signal_emit(&view->events.unmap, view); - wl_list_remove(&view->surface_new_subsurface.link); - if (view->urgent_timer) { wl_event_source_remove(view->urgent_timer); view->urgent_timer = NULL; @@ -923,183 +898,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_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) { @@ -1107,41 +905,9 @@ void view_child_init(struct sway_view_child *child, 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) { - wlr_surface_send_enter(child->surface, workspace->output->wlr_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; @@ -1151,18 +917,8 @@ void view_child_destroy(struct sway_view_child *child) { 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 { From 6fcd702c9791fc89591057a84e78da18060667bf Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 21 May 2022 14:09:01 -0400 Subject: [PATCH 19/31] 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 | 6 ++-- sway/desktop/output.c | 17 ---------- sway/desktop/xwayland.c | 58 +++++++++++++-------------------- sway/input/cursor.c | 5 +++ sway/tree/root.c | 3 -- 8 files changed, 34 insertions(+), 69 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 3411f0247..739ded61a 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -143,12 +143,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 746ae7d8b..f750d4fdf 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -7,6 +7,7 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_NON_INTERACTIVE, SWAY_SCENE_DESC_CONTAINER, 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 389bdad09..50ce6010d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -42,16 +42,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 f1c1ee087..bdb356280 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -146,6 +146,8 @@ struct sway_xdg_shell_view { struct sway_xwayland_view { struct sway_view view; + struct wlr_scene_surface *surface_scene; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -168,14 +170,12 @@ struct sway_xwayland_view { 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 map; struct wl_listener unmap; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 0932e5b97..b11dc3c79 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -169,23 +169,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 round((offset + length) * scale) - round(offset * scale); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7453c07d6..e7891ea1b 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -5,15 +5,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/tree/view.h" @@ -43,29 +44,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) { @@ -73,17 +57,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(); @@ -97,10 +82,10 @@ 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); + } struct sway_seat *seat = input_manager_current_seat(); if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { @@ -425,7 +410,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); @@ -433,15 +417,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) { @@ -486,6 +467,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); + xwayland_view->surface_scene = NULL; + wl_list_remove(&xwayland_view->commit.link); } @@ -506,6 +489,9 @@ 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_scene = wlr_scene_surface_create( + xwayland_view->view.content_tree, xsurface->surface); + transaction_commit_dirty(); } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 2ec284267..bb996304e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -86,6 +86,11 @@ struct sway_node *node_at_coords( struct sway_container *con = popup->child.view->container; return &con->node; } +#if HAVE_XWAYLAND + else if (desc->type == SWAY_SCENE_DESC_XWAYLAND_UNMANAGED) { + return NULL; + } +#endif } if (!current->parent) { diff --git a/sway/tree/root.c b/sway/tree/root.c index 666d5103c..28db0e997 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -67,9 +67,6 @@ struct sway_root *root_create(void) { root->output_layout = wlr_output_layout_create(); 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 ad01333209c425cfc3b82c45ab95704eaaf127f4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 1 Mar 2022 00:48:22 -0500 Subject: [PATCH 20/31] 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 e7891ea1b..f779234a5 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -381,17 +381,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); @@ -399,12 +388,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 3616f804c299d65b3fc49e8c0caf8cecbf7d19ba Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 27 Aug 2022 13:58:01 -0400 Subject: [PATCH 21/31] 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 | 79 +++++++++++++++++++++++--------------- 5 files changed, 83 insertions(+), 83 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index bdb356280..d3147e7e4 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 @@ -91,16 +83,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; @@ -362,4 +348,6 @@ void view_save_buffer(struct sway_view *view); bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); +void view_send_frame_done(struct sway_view *view); + #endif diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index fed25a881..48031b79d 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_node) { 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 faac1ec72..fadd00028 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -298,8 +298,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 f779234a5..2b804ccb5 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -409,8 +409,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 459d71440..a03b7732a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -47,7 +47,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); @@ -68,9 +67,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); wlr_scene_node_destroy(&view->scene_tree->node); @@ -892,10 +888,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); } void view_child_init(struct sway_view_child *child, @@ -1162,40 +1158,47 @@ 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 && wlr_surface_has_buffer(surface)) { - 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_buffer(sbuf, buffer->buffer); + 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); } 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; + } + + 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); } bool view_is_transient_for(struct sway_view *child, @@ -1203,3 +1206,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 1a09cfd95dadcfbcfe881daa36ccac16a03fd11f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 7 Mar 2022 01:03:53 -0500 Subject: [PATCH 22/31] Delete old damage tracking code The new scene graph abstraction handles this for us. --- include/sway/desktop.h | 13 -- include/sway/output.h | 33 --- 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 | 242 ---------------------- 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/output.c | 1 - sway/tree/view.c | 30 --- sway/tree/workspace.c | 1 - 21 files changed, 455 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 739ded61a..feb258739 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -45,7 +45,6 @@ struct sway_output { struct wlr_box usable_area; struct timespec last_frame; - struct wlr_output_damage *damage; int lx, ly; // layout coords int width, height; // transformed buffer size @@ -63,8 +62,6 @@ struct sway_output { struct wl_listener commit; struct wl_listener mode; struct wl_listener present; - struct wl_listener damage_destroy; - struct wl_listener damage_frame; struct wl_listener frame_request; struct { @@ -101,19 +98,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); - // this ONLY includes the enabled outputs struct sway_output *output_by_name_or_id(const char *name_or_id); @@ -128,21 +112,6 @@ void output_disable(struct sway_output *output); struct sway_workspace *output_get_active_workspace(struct sway_output *output); -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage); - -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); @@ -160,8 +129,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 db188fea4..1bc852caf 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 @@ -176,8 +171,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 d3147e7e4..471b8691d 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); @@ -264,20 +260,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 43a573093..5ff949661 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(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/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 9c7082d07..e59f092f3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -524,10 +524,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 b11dc3c79..472afd2f5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -58,127 +57,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 round((offset + length) * scale) - round(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 = round(box->x * scale); - box->y = round(box->y * scale); -} struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); @@ -379,115 +257,6 @@ static void handle_frame_request(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 && output->wlr_output && output->damage) { - wlr_output_damage_add_whole(output->damage); - } -} - -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 (ceil(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, - ceil(output->wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box.x, box.y); - wlr_output_damage_add(output->damage, &damage); - pixman_region32_fini(&damage); - - if (whole) { - wlr_output_damage_add_box(output->damage, &box); - } - - 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); - wlr_output_damage_add_box(output->damage, &box); -} - -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); - wlr_output_damage_add_box(output->damage, &box); - // 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 damage_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = - wl_container_of(listener, output, damage_destroy); - if (!output->enabled) { - return; - } - output_disable(output); - - wl_list_remove(&output->damage_destroy.link); - wl_list_remove(&output->damage_frame.link); - - transaction_commit_dirty(); -} - static void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -566,10 +335,6 @@ static void handle_mode(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } -static void update_textures(struct sway_container *con, void *data) { - container_update_title_textures(con); -} - 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; @@ -578,10 +343,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->committed & WLR_OUTPUT_STATE_SCALE) { - output_for_each_container(output, update_textures, NULL); - } - if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { arrange_layers(output); arrange_output(output); @@ -657,7 +418,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->server = server; output->scene_output = scene_output; - output->damage = wlr_output_damage_create(wlr_output); wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; @@ -669,8 +429,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->present.notify = handle_present; wl_signal_add(&wlr_output->events.frame, &output->frame_request); output->frame_request.notify = handle_frame_request; - wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); - output->damage_destroy.notify = damage_handle_destroy; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 48031b79d..95e7e847d 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 fadd00028..92bfd239f 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -198,24 +198,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) { @@ -263,8 +245,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 656d4bd50..089570fab 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 da4163a98..cf6b69c36 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -515,12 +515,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/output.c b/sway/tree/output.c index ad7abcfdf..ffe372f9d 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" diff --git a/sway/tree/view.c b/sway/tree/view.c index a03b7732a..0f1ea1494 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/input/cursor.h" @@ -440,34 +439,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) { @@ -1144,7 +1115,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 c0aa651a0..b1d6fe788 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -701,7 +701,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 dc511e3ccde67f602146179750859f6e03dc5f27 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 21 Feb 2022 16:58:37 -0500 Subject: [PATCH 23/31] container: Don't track outputs The scene graph abstraction does this for us --- include/sway/tree/container.h | 12 ------ sway/desktop/transaction.c | 4 -- sway/tree/container.c | 78 ----------------------------------- sway/tree/output.c | 10 ----- 4 files changed, 104 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 1bc852caf..041c1cc55 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. @@ -272,15 +269,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/transaction.c b/sway/desktop/transaction.c index 95e7e847d..ef54aa5f7 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/tree/container.c b/sway/tree/container.c index cf6b69c36..244b156d5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -115,7 +115,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(&root->events.new_node, &c->node); @@ -371,7 +370,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); @@ -515,17 +513,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] @@ -1240,71 +1227,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 wlr_output *wlr_output = data; - wlr_surface_send_enter(surface, wlr_output); -} - -static void surface_send_leave_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_leave(surface, wlr_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, - }; - 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]; - 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->wlr_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->wlr_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); - } - } - 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); - } -} - 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 ffe372f9d..e7cde267e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -284,14 +284,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; @@ -306,8 +298,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; From 90df29dde3a9aa041235d48539de39a3fea1ce54 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 14 Oct 2022 19:32:38 -0400 Subject: [PATCH 24/31] scene_graph: Arrange scene graph on transaction apply --- sway/desktop/transaction.c | 444 ++++++++++++++++++++++++++++++++++++- 1 file changed, 443 insertions(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ef54aa5f7..71a0f0256 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,446 @@ 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); + + wlr_scene_node_set_enabled(&con->title_bar.tree->node, height); + if (height) { + int titlebar_border_thickness = config->titlebar_border_thickness; + + wlr_scene_rect_set_size(con->title_bar.border, width, height); + wlr_scene_rect_set_size(con->title_bar.background, + width - titlebar_border_thickness * 2, + height - titlebar_border_thickness * 2); + + wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); + wlr_scene_node_set_position(&con->title_bar.background->node, + titlebar_border_thickness, titlebar_border_thickness); + + 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 bool 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 { + return false; + } + + return true; +} + +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); + } + + if (!arrange_children(con->current.layout, con->current.children, + con->current.focused_inactive_child, con->content_tree, + width, height, gaps)) { + sway_assert(false, "unreachable"); + } + } +} + +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) { + if (!arrange_children(ws->current.layout, ws->current.tiling, + ws->current.focused_inactive_child, ws->layers.tiling, + width, height, ws->gaps_inner)) { + sway_assert(false, "unreachable"); + } +} + +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_scene_descriptor *desc = node->data; + + // 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 (desc) { + sway_assert(desc->type == SWAY_SCENE_DESC_POPUP, + "Corrupted scene tree: expected a popup node"); + struct sway_xdg_popup *popup = desc->data; + struct wlr_scene_tree *tree = popup->child.view->content_tree; + + int lx, ly; + wlr_scene_node_coords(&tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->child.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); + wlr_scene_node_set_enabled(&root->layers.shell_overlay->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. */ @@ -305,6 +746,7 @@ static void transaction_progress(void) { return; } transaction_apply(server.queued_transaction); + arrange_root(root); transaction_destroy(server.queued_transaction); server.queued_transaction = NULL; @@ -383,7 +825,7 @@ static void transaction_commit(struct sway_transaction *transaction) { view_send_frame_done(node->sway_container->view); } if (!hidden && node_is_view(node) && - !node->sway_container->view->saved_surface_node) { + !node->sway_container->view->saved_surface_tree) { view_save_buffer(node->sway_container->view); } node->instruction = instruction; From 625cf6df3b5cb113a2b16fb52063f997831b69dd Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 3 May 2022 21:53:30 -0400 Subject: [PATCH 25/31] transaction: Rebase cursor on transaction finish We need to make sure that we rebase the cursor after the scene graph is arranged otherwise the mouse will rebase on the old state instead. --- sway/desktop/transaction.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 71a0f0256..79444cdc3 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -732,8 +732,6 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - - cursor_rebase_all(); } static void transaction_commit_pending(void); @@ -747,6 +745,7 @@ static void transaction_progress(void) { } transaction_apply(server.queued_transaction); arrange_root(root); + cursor_rebase_all(); transaction_destroy(server.queued_transaction); server.queued_transaction = NULL; From d1417b8a13ec5842e48fccdced2688a469df5f80 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 22 Feb 2022 00:50:18 -0500 Subject: [PATCH 26/31] output: destroy output after ensuring it is disabled This became necessary after the removal of the wlr_output_damage object. Before, the wlr_output_damage destroy signal fired disabling the output, then after the handle_destroy function would be called. Fix this to prevent hitting an assert. --- sway/desktop/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 472afd2f5..a0e20f950 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -286,11 +286,12 @@ static void update_output_manager_config(struct sway_server *server) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_server *server = output->server; - output_begin_destroy(output); if (output->enabled) { output_disable(output); } + + output_begin_destroy(output); wl_list_remove(&output->link); From 26e0ccc3622a48259abd2c6e772db592336aed55 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 18 Mar 2022 08:42:30 -0400 Subject: [PATCH 27/31] 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 +++ sway/tree/container.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 286bbeba5..5eb3d8e8c 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -54,6 +54,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; diff --git a/sway/tree/container.c b/sway/tree/container.c index 244b156d5..3a53854dd 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1244,6 +1244,9 @@ list_t *container_get_siblings(struct sway_container *container) { if (container_is_scratchpad_hidden(container)) { return NULL; } + if (!container->pending.workspace) { + return NULL; + } if (list_find(container->pending.workspace->tiling, container) != -1) { return container->pending.workspace->tiling; } From b581b30609392df9163a7a064d038984c93321f0 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 30 Apr 2022 17:09:44 -0400 Subject: [PATCH 28/31] 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 5acea791a..452fd83e4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -144,13 +144,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 997a2f464..513d35651 100644 --- a/sway/main.c +++ b/sway/main.c @@ -192,11 +192,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; @@ -204,8 +200,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 25f1fc6c9ce5257ad386c8352aef4abbf7a3f649 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 7 Aug 2022 20:16:08 -0400 Subject: [PATCH 29/31] 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 | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 041c1cc55..b750bb35c 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 79444cdc3..9b3057b99 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -393,6 +393,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 3a53854dd..f4bfd5085 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -32,6 +32,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, int x, int y) { + return false; +} + static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, bool *failed) { if (*failed) { @@ -69,6 +98,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 alloc_failure = false; c->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); @@ -85,6 +115,21 @@ struct sway_container *container_create(struct sway_view *view) { c->border.bottom = alloc_rect_node(c->border.tree, &alloc_failure); c->border.left = alloc_rect_node(c->border.tree, &alloc_failure); c->border.right = alloc_rect_node(c->border.tree, &alloc_failure); + + c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); + if (!c->output_handler) { + alloc_failure = true; + } + + if (!alloc_failure) { + 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; + } } else { c->pending.children = create_list(); c->current.children = create_list(); @@ -375,6 +420,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 f167200022eef116ace77ae6563617b3ce0367db Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 7 Nov 2022 20:21:38 +0300 Subject: [PATCH 30/31] chore: chase wlroots fractional coords update --- include/sway/output.h | 2 +- include/sway/tree/view.h | 8 ++++---- sway/desktop/layer_shell.c | 28 +++++++++++++++++----------- sway/desktop/output.c | 2 +- sway/desktop/transaction.c | 4 ++-- sway/desktop/xdg_shell.c | 10 +++++----- sway/desktop/xwayland.c | 4 ++-- sway/tree/arrange.c | 4 ++-- sway/tree/container.c | 2 +- sway/tree/view.c | 8 ++++---- 10 files changed, 39 insertions(+), 33 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index feb258739..31b1e220f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -42,7 +42,7 @@ struct sway_output { struct sway_server *server; struct wl_list link; - struct wlr_box usable_area; + struct wlr_fbox usable_area; struct timespec last_frame; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 471b8691d..e0a81b18f 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -40,7 +40,7 @@ struct sway_view_impl { enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*configure)(struct sway_view *view, double lx, double ly, - int width, int height); + double width, double height); void (*set_activated)(struct sway_view *view, bool activated); void (*set_tiled)(struct sway_view *view, bool tiled); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); @@ -81,7 +81,7 @@ struct sway_view { // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. - struct wlr_box geometry; + struct wlr_fbox geometry; struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; @@ -217,8 +217,8 @@ const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height); -uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, - int height); +uint32_t view_configure(struct sway_view *view, double lx, double ly, + double width, double height); bool view_inhibit_idle(struct sway_view *view); diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 68659b150..8d18f981e 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -19,8 +19,8 @@ #include "sway/tree/workspace.h" #include -static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, - struct wlr_box *usable_area, struct wlr_scene_tree *tree) { +static void arrange_surface(struct sway_output *output, const struct wlr_fbox *full_area, + struct wlr_fbox *usable_area, struct wlr_scene_tree *tree) { struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { struct sway_scene_descriptor *desc = node->data; @@ -40,10 +40,16 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu } void arrange_layers(struct sway_output *output) { - struct wlr_box usable_area = { 0 }; + int output_width, output_height; wlr_output_effective_resolution(output->wlr_output, - &usable_area.width, &usable_area.height); - const struct wlr_box full_area = usable_area; + &output_width, &output_height); + struct wlr_fbox usable_area = { + .x = 0, + .y = 0, + .width = output_width, + .height = output_height, + }; + const struct wlr_fbox full_area = usable_area; arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); @@ -51,9 +57,9 @@ void arrange_layers(struct sway_output *output) { arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); if (memcmp(&usable_area, &output->usable_area, - sizeof(struct wlr_box)) != 0) { + sizeof(struct wlr_fbox)) != 0) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); - memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_fbox)); arrange_output(output); } } @@ -182,7 +188,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } - int lx, ly; + double lx, ly; wlr_scene_node_coords(&surface->scene->tree->node, &lx, &ly); wlr_scene_node_set_position(&surface->popups->node, lx, ly); } @@ -238,12 +244,12 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; struct sway_output *output = popup->toplevel->output; - int lx, ly; + double 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 = { + struct wlr_fbox output_toplevel_sx_box = { .x = output->lx - lx, .y = output->ly - ly, .width = output->width, @@ -300,7 +306,7 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *layer_surface = data; sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 - " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", + " size %lfx%lf margin %lf,%lf,%lf,%lf", layer_surface->namespace, layer_surface->pending.layer, layer_surface->pending.anchor, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a0e20f950..db768ac44 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -121,7 +121,7 @@ static struct buffer_timer *buffer_timer_assign(struct wlr_scene_buffer *buffer) } static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, - int x, int y, void *user_data) { + double x, double y, void *user_data) { struct send_frame_done_data *data = user_data; struct sway_output *output = data->output; int view_max_render_time = 0; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 9b3057b99..067ea0cff 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -599,7 +599,7 @@ static void arrange_output(struct sway_output *output, int width, int height) { arrange_fullscreen(child->layers.fullscreen, fs, child, width, height); } else { - struct wlr_box *area = &output->usable_area; + struct wlr_fbox *area = &output->usable_area; struct side_gaps *gaps = &child->current_gaps; wlr_scene_node_set_position(&child->layers.tiling->node, @@ -631,7 +631,7 @@ static void arrange_popup(struct wlr_scene_tree *popup) { struct sway_xdg_popup *popup = desc->data; struct wlr_scene_tree *tree = popup->child.view->content_tree; - int lx, ly; + double lx, ly; wlr_scene_node_coords(&tree->node, &lx, &ly); wlr_scene_node_set_position(&popup->child.scene_tree->node, lx, ly); } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 92bfd239f..0cec74561 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -63,7 +63,7 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { // the output box expressed in the coordinate system of the toplevel parent // of the popup - struct wlr_box output_toplevel_sx_box = { + struct wlr_fbox output_toplevel_sx_box = { .x = output->lx - view->container->pending.content_x + view->geometry.x, .y = output->ly - view->container->pending.content_y + view->geometry.y, .width = output->width, @@ -146,7 +146,7 @@ static const char *get_string_prop(struct sway_view *view, } static uint32_t configure(struct sway_view *view, double lx, double ly, - int width, int height) { + double width, double height) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { @@ -257,7 +257,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; - struct wlr_box new_geo; + struct wlr_fbox new_geo; wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); bool new_size = new_geo.width != view->geometry.width || new_geo.height != view->geometry.height || @@ -268,7 +268,7 @@ 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. - memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_fbox)); if (container_is_floating(view->container)) { view_update_size(view); transaction_commit_dirty_client(); @@ -317,7 +317,7 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { return; } - int lx, ly; + double lx, ly; wlr_scene_node_coords(&popup->child.view->content_tree->node, &lx, &ly); wlr_scene_node_set_position(&popup->child.scene_tree->node, lx, ly); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 2b804ccb5..0f88ca6e0 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -232,8 +232,8 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { } } -static uint32_t configure(struct sway_view *view, double lx, double ly, int width, - int height) { +static uint32_t configure(struct sway_view *view, double lx, double ly, + double width, double height) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return 0; diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 9c1a11e55..6ce1bdd64 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -259,8 +259,8 @@ void arrange_workspace(struct sway_workspace *workspace) { return; } struct sway_output *output = workspace->output; - struct wlr_box *area = &output->usable_area; - sway_log(SWAY_DEBUG, "Usable area for ws: %dx%d@%d,%d", + struct wlr_fbox *area = &output->usable_area; + sway_log(SWAY_DEBUG, "Usable area for ws: %lfx%lf@%lf,%lf", area->width, area->height, area->x, area->y); bool first_arrange = workspace->width == 0 && workspace->height == 0; diff --git a/sway/tree/container.c b/sway/tree/container.c index f4bfd5085..7abd6802a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -57,7 +57,7 @@ static void handle_output_leave( } static bool handle_point_accepts_input( - struct wlr_scene_buffer *buffer, int x, int y) { + struct wlr_scene_buffer *buffer, double x, double y) { return false; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 0f1ea1494..2beb55792 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -170,8 +170,8 @@ void view_get_constraints(struct sway_view *view, double *min_width, } } -uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, - int height) { +uint32_t view_configure(struct sway_view *view, double lx, double ly, + double width, double height) { if (view->impl->configure) { return view->impl->configure(view, lx, ly, width, height); } @@ -1138,7 +1138,7 @@ void view_remove_saved_buffer(struct sway_view *view) { } static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, - int sx, int sy, void *data) { + double sx, double sy, void *data) { struct wlr_scene_tree *tree = data; struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); @@ -1178,7 +1178,7 @@ bool view_is_transient_for(struct sway_view *child, } static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, - int x, int y, void *data) { + double x, double y, void *data) { struct timespec *when = data; wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); } From cb4205185c70894f95301196f30c832ffeddd5cd Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 7 Nov 2022 20:40:00 +0300 Subject: [PATCH 31/31] Add wp-fractional-scale-v1 support --- sway/server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/server.c b/sway/server.c index 79b61d796..1804a38c8 100644 --- a/sway/server.c +++ b/sway/server.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -201,6 +202,8 @@ bool server_init(struct sway_server *server) { wlr_viewporter_create(server->wl_display); wlr_single_pixel_buffer_manager_v1_create(server->wl_display); + wlr_fractional_scale_manager_v1_create(server->wl_display); + struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);