From 59c94887018bdfa578c4371c4275061ca6e71b3e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 3 Jun 2018 16:35:06 +1000 Subject: WIP: Atomic layout updates ground work --- include/sway/desktop/transaction.h | 56 ++++++++++++++++++++++++++++++++++++++ include/sway/output.h | 2 ++ include/sway/tree/arrange.h | 33 ++++++++++++++++------ include/sway/tree/container.h | 29 ++++++++++++++++++++ include/sway/tree/view.h | 19 ++++++------- 5 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 include/sway/desktop/transaction.h (limited to 'include') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h new file mode 100644 index 00000000..575d28c8 --- /dev/null +++ b/include/sway/desktop/transaction.h @@ -0,0 +1,56 @@ +#ifndef _SWAY_TRANSACTION_H +#define _SWAY_TRANSACTION_H +#include "sway/tree/container.h" + +/** + * Transactions enable us to perform atomic layout updates. + * + * When we want to make adjustments to the layout, we create a transaction. + * A transaction contains a list of affected containers and their new state. + * A state might contain a new size, or new border settings, or new parent/child + * relationships. + * + * Calling transaction_commit() makes sway notify of all the affected clients + * with their new sizes. We then wait for all the views to respond with their + * new surface sizes. When all are ready, or when a timeout has passed, we apply + * the updates all at the same time. + */ + +struct sway_transaction { + struct wl_event_source *timer; + list_t *instructions; // struct sway_transaction_instruction * + list_t *damage; // struct wlr_box * + size_t num_waiting; +}; + +/** + * Create a new transaction. + */ +struct sway_transaction *transaction_create(void); + +/** + * Add a container's pending state to the transaction. + */ +void transaction_add_container(struct sway_transaction *transaction, + struct sway_container *container); + +/** + * Add a box to be damaged when the transaction is applied. + * The box should be in layout coordinates. + */ +void transaction_add_damage(struct sway_transaction *transaction, + struct wlr_box *box); + +/** + * Submit a transaction to the client views for configuration. + */ +void transaction_commit(struct sway_transaction *transaction); + +/** + * 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. + */ +void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); + +#endif diff --git a/include/sway/output.h b/include/sway/output.h index 70f746dc..e6ca0d02 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -42,6 +42,8 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, 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); diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index ce95cfe9..23cd66dc 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -1,18 +1,33 @@ #ifndef _SWAY_ARRANGE_H #define _SWAY_ARRANGE_H +#include "sway/desktop/transaction.h" struct sway_container; -// Determine the root container's geometry, then iterate to everything below -void arrange_root(void); - -// Determine the output's geometry, then iterate to everything below -void arrange_output(struct sway_container *output); +/** + * Arrange layout for all the children of the given container, and add them to + * the given transaction. + * + * Use this function if you need to arrange multiple sections of the tree in one + * transaction. + */ +void arrange_windows(struct sway_container *container, + struct sway_transaction *transaction); -// Determine the workspace's geometry, then iterate to everything below -void arrange_workspace(struct sway_container *workspace); +/** + * Arrange layout for the given container and commit the transaction. + * + * This function is a wrapper around arrange_windows, and handles creating and + * committing the transaction for you. Use this function if you're only doing + * one arrange operation. + */ +void arrange_and_commit(struct sway_container *container); -// Arrange layout for all the children of the given workspace/container -void arrange_children_of(struct sway_container *parent); +// These functions are temporary and are only here to make everything compile. +// They are wrappers around arrange_and_commit. +void arrange_root(void); +void arrange_output(struct sway_container *container); +void arrange_workspace(struct sway_container *container); +void arrange_children_of(struct sway_container *container); #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 7ed6aab1..dd5bd47c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -54,6 +54,28 @@ struct sway_output; struct sway_workspace; struct sway_view; +struct sway_container_state { + // Container/swayc properties + enum sway_container_layout layout; + double swayc_x, swayc_y; + double swayc_width, swayc_height; + + //struct sway_container *parent; + //list_t *children; + + // View properties + double view_x, view_y; + double view_width, view_height; + bool is_fullscreen; + + enum sway_container_border border; + int border_thickness; + bool border_top; + bool border_bottom; + bool border_left; + bool border_right; +}; + struct sway_container { union { // TODO: Encapsulate state for other node types as well like C_CONTAINER @@ -69,6 +91,8 @@ struct sway_container { */ size_t id; + struct sway_container_state pending; + char *name; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar @@ -246,4 +270,9 @@ void container_set_geometry_from_floating_view(struct sway_container *con); */ bool container_is_floating(struct sway_container *container); +/** + * Get a container's box in layout coordinates. + */ +struct wlr_box *container_get_box(struct sway_container *container); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 3df38e2d..f47db567 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -29,8 +29,8 @@ struct sway_view_impl { const char *(*get_string_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); - void (*configure)(struct sway_view *view, double lx, double ly, int width, - int height); + uint32_t (*configure)(struct sway_view *view, double lx, double ly, + int width, int height); void (*set_activated)(struct sway_view *view, bool activated); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); bool (*wants_floating)(struct sway_view *view); @@ -70,6 +70,12 @@ struct sway_view { list_t *executed_criteria; // struct criteria * list_t *marks; // char * + list_t *instructions; // struct sway_transaction_instruction * + + // If saved_texture is set, the main surface of the view will render this + // texture instead of its own. This is used while waiting for transactions + // to complete. + struct wlr_texture *saved_texture; struct wlr_texture *marks_focused; struct wlr_texture *marks_focused_inactive; @@ -103,8 +109,6 @@ struct sway_xdg_shell_v6_view { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - - int pending_width, pending_height; }; struct sway_xdg_shell_view { @@ -119,8 +123,6 @@ struct sway_xdg_shell_view { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - - int pending_width, pending_height; }; struct sway_xwayland_view { @@ -138,9 +140,6 @@ struct sway_xwayland_view { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - - int pending_lx, pending_ly; - int pending_width, pending_height; }; struct sway_xwayland_unmanaged { @@ -212,7 +211,7 @@ uint32_t view_get_window_type(struct sway_view *view); const char *view_get_shell(struct sway_view *view); -void view_configure(struct sway_view *view, double ox, double oy, int width, +uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); /** -- cgit v1.2.3-54-g00ecf From f9e6d703d298dbdee0770fd9e0c64ab2d7ac7deb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Jun 2018 19:19:30 +1000 Subject: Make main properties be the pending state --- include/sway/tree/container.h | 4 +- sway/desktop/desktop.c | 3 +- sway/desktop/output.c | 28 ++++++----- sway/desktop/transaction.c | 49 ++++++++++--------- sway/desktop/xwayland.c | 9 ++-- sway/tree/arrange.c | 107 +++++++++++++++++++----------------------- sway/tree/layout.c | 4 +- sway/tree/view.c | 90 +++++++++++++++++------------------ sway/tree/workspace.c | 6 --- 9 files changed, 143 insertions(+), 157 deletions(-) (limited to 'include') diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index dd5bd47c..a8efd893 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -91,7 +91,9 @@ struct sway_container { */ size_t id; - struct sway_container_state pending; + // The pending state is the main container properties, and the current state is in the below struct. + // This means most places of the code can refer to the main variables (pending state) and it'll just work. + struct sway_container_state current; char *name; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index 66f33151..e495790c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -7,7 +7,8 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *cont = root_container.children->items[i]; if (cont->type == C_OUTPUT) { - output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y, + output_damage_surface(cont->sway_output, + lx - cont->current.swayc_x, ly - cont->current.swayc_y, surface, whole); } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c5d445a6..8af05bc3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -774,9 +774,10 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = con->y + container_titlebar_height() * i; - render_titlebar(output, damage, child, child->x, y, child->width, - colors, title_texture, marks_texture); + int y = con->current.swayc_y + container_titlebar_height() * i; + render_titlebar(output, damage, child, child->current.swayc_x, y, + child->current.swayc_width, colors, + title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -795,7 +796,7 @@ static void render_container_stacked(struct sway_output *output, static void render_container(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - switch (con->layout) { + switch (con->current.layout) { case L_NONE: case L_HORIZ: case L_VERT: @@ -832,9 +833,10 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (con->sway_view->border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->x, con->y, con->width, - colors, title_texture, marks_texture); + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); } else { render_top_border(soutput, damage, con, colors); } @@ -1184,8 +1186,8 @@ void output_damage_from_view(struct sway_output *output, 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->swayc->x; - box.y -= output->swayc->y; + box.x -= output->swayc->current.swayc_x; + box.y -= output->swayc->current.swayc_y; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } @@ -1204,10 +1206,10 @@ static void output_damage_whole_container_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { struct wlr_box box = { - .x = con->x - output->wlr_output->lx, - .y = con->y - output->wlr_output->ly, - .width = con->width, - .height = con->height, + .x = con->current.swayc_x - output->wlr_output->lx, + .y = con->current.swayc_y - output->wlr_output->ly, + .width = con->current.swayc_width, + .height = con->current.swayc_height, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 69f97e3d..313e707b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -67,8 +67,30 @@ void transaction_add_container(struct sway_transaction *transaction, calloc(1, sizeof(struct sway_transaction_instruction)); instruction->transaction = transaction; instruction->container = container; - memcpy(&instruction->state, &container->pending, - sizeof(struct sway_container_state)); + + // Copy the container's main (pending) properties into the instruction state + struct sway_container_state *state = &instruction->state; + state->layout = container->layout; + state->swayc_x = container->x; + state->swayc_y = container->y; + state->swayc_width = container->width; + state->swayc_height = container->height; + + if (container->type == C_VIEW) { + struct sway_view *view = container->sway_view; + state->view_x = view->x; + state->view_y = view->y; + state->view_width = view->width; + state->view_height = view->height; + state->is_fullscreen = view->is_fullscreen; + state->border = view->border; + state->border_thickness = view->border_thickness; + state->border_top = view->border_top; + state->border_left = view->border_left; + state->border_right = view->border_right; + state->border_bottom = view->border_bottom; + } + list_add(transaction->instructions, instruction); } @@ -102,30 +124,13 @@ static void transaction_apply(struct sway_transaction *transaction) { for (i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - struct sway_container_state *state = &instruction->state; struct sway_container *container = instruction->container; - container->layout = state->layout; - container->x = state->swayc_x; - container->y = state->swayc_y; - container->width = state->swayc_width; - container->height = state->swayc_height; + memcpy(&instruction->container->current, &instruction->state, + sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - struct sway_view *view = container->sway_view; - view->x = state->view_x; - view->y = state->view_y; - view->width = state->view_width; - view->height = state->view_height; - view->is_fullscreen = state->is_fullscreen; - view->border = state->border; - view->border_thickness = state->border_thickness; - view->border_top = state->border_top; - view->border_left = state->border_left; - view->border_right = state->border_right; - view->border_bottom = state->border_bottom; - - remove_saved_view_texture(view); + remove_saved_view_texture(container->sway_view); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6a3c1b66..d8442530 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -257,11 +257,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { if (view->swayc && container_is_floating(view->swayc)) { view_update_size(view, xsurface->width, xsurface->height); } else { - view_update_size(view, view->swayc->pending.swayc_width, - view->swayc->pending.swayc_height); + view_update_size(view, view->swayc->width, view->swayc->height); } - view_update_position(view, - view->swayc->pending.view_x, view->swayc->pending.view_y); + view_update_position(view, view->x, view->y); view_damage_from(view); } } @@ -314,7 +312,8 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } // TODO: Let floating views do whatever - configure(view, view->swayc->x, view->swayc->y, view->width, view->height); + configure(view, view->swayc->current.view_x, view->swayc->current.view_y, + view->swayc->current.view_width, view->swayc->current.view_height); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index d8b3aec1..cf7ce61c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -22,48 +22,46 @@ static void apply_horiz_layout(struct sway_container *parent) { return; } size_t parent_offset = 0; - if (parent->parent->pending.layout == L_TABBED) { + if (parent->parent->layout == L_TABBED) { parent_offset = container_titlebar_height(); - } else if (parent->parent->pending.layout == L_STACKED) { + } else if (parent->parent->layout == L_STACKED) { parent_offset = container_titlebar_height() * parent->parent->children->length; } - size_t parent_height = parent->pending.swayc_height - parent_offset; + size_t parent_height = parent->height - parent_offset; // Calculate total width of children double total_width = 0; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; - if (child->pending.swayc_width <= 0) { + if (child->width <= 0) { if (num_children > 1) { - child->pending.swayc_width = - parent->pending.swayc_width / (num_children - 1); + child->width = parent->width / (num_children - 1); } else { - child->pending.swayc_width = parent->pending.swayc_width; + child->width = parent->width; } } - total_width += child->pending.swayc_width; + total_width += child->width; } - double scale = parent->pending.swayc_width / total_width; + double scale = parent->width / total_width; // Resize windows wlr_log(L_DEBUG, "Arranging %p horizontally", parent); - double child_x = parent->pending.swayc_x; + double child_x = parent->x; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; wlr_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", - child, child->type, child->pending.swayc_width, scale); - child->pending.swayc_x = child_x; - child->pending.swayc_y = parent->pending.swayc_y + parent_offset; - child->pending.swayc_width = floor(child->pending.swayc_width * scale); - child->pending.swayc_height = parent_height; - child_x += child->pending.swayc_width; + child, child->type, child->width, scale); + child->x = child_x; + child->y = parent->y + parent_offset; + child->width = floor(child->width * scale); + child->height = parent_height; + child_x += child->width; // Make last child use remaining width of parent if (i == num_children - 1) { - child->pending.swayc_width = parent->pending.swayc_x + - parent->pending.swayc_width - child->pending.swayc_x; + child->width = parent->x + parent->width - child->x; } } } @@ -74,49 +72,47 @@ static void apply_vert_layout(struct sway_container *parent) { return; } size_t parent_offset = 0; - if (parent->parent->pending.layout == L_TABBED) { + if (parent->parent->layout == L_TABBED) { parent_offset = container_titlebar_height(); - } else if (parent->parent->pending.layout == L_STACKED) { + } else if (parent->parent->layout == L_STACKED) { parent_offset = container_titlebar_height() * parent->parent->children->length; } - size_t parent_height = parent->pending.swayc_height - parent_offset; + size_t parent_height = parent->height - parent_offset; // Calculate total height of children double total_height = 0; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; - if (child->pending.swayc_height <= 0) { + if (child->height <= 0) { if (num_children > 1) { - child->pending.swayc_height = - parent_height / (num_children - 1); + child->height = parent_height / (num_children - 1); } else { - child->pending.swayc_height = parent_height; + child->height = parent_height; } } - total_height += child->pending.swayc_height; + total_height += child->height; } double scale = parent_height / total_height; // Resize wlr_log(L_DEBUG, "Arranging %p vertically", parent); - double child_y = parent->pending.swayc_y + parent_offset; + double child_y = parent->y + parent_offset; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; wlr_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", - child, child->type, child->pending.swayc_height, scale); - child->pending.swayc_x = parent->pending.swayc_x; - child->pending.swayc_y = child_y; - child->pending.swayc_width = parent->pending.swayc_width; - child->pending.swayc_height = - floor(child->pending.swayc_height * scale); - child_y += child->pending.swayc_height; + child, child->type, child->height, scale); + child->x = parent->x; + child->y = child_y; + child->width = parent->width; + child->height = floor(child->height * scale); + child_y += child->height; // Make last child use remaining height of parent if (i == num_children - 1) { - child->pending.swayc_height = parent->pending.swayc_y + - parent_offset + parent_height - child->pending.swayc_y; + child->height = + parent->y + parent_offset + parent_height - child->y; } } } @@ -126,19 +122,19 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { return; } size_t parent_offset = 0; - if (parent->parent->pending.layout == L_TABBED) { + if (parent->parent->layout == L_TABBED) { parent_offset = container_titlebar_height(); - } else if (parent->parent->pending.layout == L_STACKED) { + } else if (parent->parent->layout == L_STACKED) { parent_offset = container_titlebar_height() * parent->parent->children->length; } - size_t parent_height = parent->pending.swayc_height - parent_offset; + size_t parent_height = parent->height - parent_offset; for (int i = 0; i < parent->children->length; ++i) { struct sway_container *child = parent->children->items[i]; - child->pending.swayc_x = parent->pending.swayc_x; - child->pending.swayc_y = parent->pending.swayc_y + parent_offset; - child->pending.swayc_width = parent->pending.swayc_width; - child->pending.swayc_height = parent_height; + child->x = parent->x; + child->y = parent->y + parent_offset; + child->width = parent->width; + child->height = parent_height; } } @@ -148,11 +144,10 @@ static void _arrange_children_of(struct sway_container *parent, return; } wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, - parent->name, parent->pending.swayc_width, parent->pending.swayc_height, - parent->pending.swayc_x, parent->pending.swayc_y); + parent->name, parent->width, parent->height, parent->x, parent->y); // Calculate x, y, width and height of children - switch (parent->pending.layout) { + switch (parent->layout) { case L_HORIZ: apply_horiz_layout(parent); break; @@ -191,13 +186,13 @@ static void _arrange_workspace(struct sway_container *workspace, struct wlr_box *area = &output->sway_output->usable_area; wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", area->width, area->height, area->x, area->y); - workspace->pending.swayc_width = area->width; - workspace->pending.swayc_height = area->height; - workspace->pending.swayc_x = output->x + area->x; - workspace->pending.swayc_y = output->y + area->y; + workspace->width = area->width; + workspace->height = area->height; + workspace->x = output->x + area->x; + workspace->y = output->y + area->y; transaction_add_container(transaction, workspace); wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, - workspace->pending.swayc_x, workspace->pending.swayc_y); + workspace->x, workspace->y); _arrange_children_of(workspace, transaction); } @@ -213,13 +208,9 @@ static void _arrange_output(struct sway_container *output, output->y = output_box->y; output->width = output_box->width; output->height = output_box->height; - output->pending.swayc_x = output_box->x; - output->pending.swayc_y = output_box->y; - output->pending.swayc_width = output_box->width; - output->pending.swayc_height = output_box->height; transaction_add_container(transaction, output); wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", - output->name, output->pending.swayc_x, output->pending.swayc_y); + output->name, output->x, output->y); for (int i = 0; i < output->children->length; ++i) { struct sway_container *workspace = output->children->items[i]; _arrange_workspace(workspace, transaction); @@ -238,10 +229,6 @@ static void _arrange_root(struct sway_transaction *transaction) { root_container.y = layout_box->y; root_container.width = layout_box->width; root_container.height = layout_box->height; - root_container.pending.swayc_x = layout_box->x; - root_container.pending.swayc_y = layout_box->y; - root_container.pending.swayc_width = layout_box->width; - root_container.pending.swayc_height = layout_box->height; transaction_add_container(transaction, &root_container); for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 3bba049a..6d4cd088 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -915,10 +915,10 @@ void container_recursive_resize(struct sway_container *container, bool layout_match = true; wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { - container->pending.swayc_width += amount; + container->width += amount; layout_match = container->layout == L_HORIZ; } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { - container->pending.swayc_height += amount; + container->height += amount; layout_match = container->layout == L_VERT; } if (container->children) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 40fe2740..dbf803c6 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -187,23 +187,23 @@ void view_autoconfigure(struct sway_view *view) { } } - struct sway_container_state *state = &view->swayc->pending; + struct sway_container *con = view->swayc; - state->border_top = state->border_bottom = true; - state->border_left = state->border_right = true; + view->border_top = view->border_bottom = true; + view->border_left = view->border_right = true; if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_VERTICAL || (config->hide_edge_borders == E_SMART && !other_views)) { - state->border_left = state->swayc_x != ws->x; - int right_x = state->swayc_x + state->swayc_width; - state->border_right = right_x != ws->x + ws->width; + view->border_left = con->x != ws->x; + int right_x = con->x + con->width; + view->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_HORIZONTAL || (config->hide_edge_borders == E_SMART && !other_views)) { - state->border_top = state->swayc_y != ws->y; - int bottom_y = state->swayc_y + state->swayc_height; - state->border_bottom = bottom_y != ws->y + ws->height; + view->border_top = con->y != ws->y; + int bottom_y = con->y + con->height; + view->border_bottom = bottom_y != ws->y + ws->height; } double x, y, width, height; @@ -213,54 +213,53 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the swayc's y is the top of the title // area. We have to offset the surface y by the height of the title bar, and // disable any top border because we'll always have the title bar. - if (view->swayc->parent->pending.layout == L_TABBED) { + if (con->parent->layout == L_TABBED) { y_offset = container_titlebar_height(); - state->border_top = false; - } else if (view->swayc->parent->pending.layout == L_STACKED) { - y_offset = container_titlebar_height() - * view->swayc->parent->children->length; + view->border_top = false; + } else if (con->parent->layout == L_STACKED) { + y_offset = container_titlebar_height() * con->parent->children->length; view->border_top = false; } - switch (state->border) { + switch (view->border) { case B_NONE: - x = state->swayc_x; - y = state->swayc_y + y_offset; - width = state->swayc_width; - height = state->swayc_height - y_offset; + x = con->x; + y = con->y + y_offset; + width = con->width; + height = con->height - y_offset; break; case B_PIXEL: - x = state->swayc_x + state->border_thickness * state->border_left; - y = state->swayc_y + state->border_thickness * state->border_top + y_offset; - width = state->swayc_width - - state->border_thickness * state->border_left - - state->border_thickness * state->border_right; - height = state->swayc_height - y_offset - - state->border_thickness * state->border_top - - state->border_thickness * state->border_bottom; + x = con->x + view->border_thickness * view->border_left; + y = con->y + view->border_thickness * view->border_top + y_offset; + width = con->width + - view->border_thickness * view->border_left + - view->border_thickness * view->border_right; + height = con->height - y_offset + - view->border_thickness * view->border_top + - view->border_thickness * view->border_bottom; break; case B_NORMAL: // Height is: 1px border + 3px pad + title height + 3px pad + 1px border - x = state->swayc_x + state->border_thickness * state->border_left; - width = state->swayc_width - - state->border_thickness * state->border_left - - state->border_thickness * state->border_right; + x = con->x + view->border_thickness * view->border_left; + width = con->width + - view->border_thickness * view->border_left + - view->border_thickness * view->border_right; if (y_offset) { - y = state->swayc_y + y_offset; - height = state->swayc_height - y_offset - - state->border_thickness * state->border_bottom; + y = con->y + y_offset; + height = con->height - y_offset + - view->border_thickness * view->border_bottom; } else { - y = state->swayc_y + container_titlebar_height(); - height = state->swayc_height - container_titlebar_height() - - state->border_thickness * state->border_bottom; + y = con->y + container_titlebar_height(); + height = con->height - container_titlebar_height() + - view->border_thickness * view->border_bottom; } break; } - state->view_x = x; - state->view_y = y; - state->view_width = width; - state->view_height = height; + view->x = x; + view->y = y; + view->width = width; + view->height = height; } void view_set_activated(struct sway_view *view, bool activated) { @@ -319,9 +318,8 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { view_configure(view, view->saved_x, view->saved_y, view->saved_width, view->saved_height); } else { - view->swayc->width = view->swayc->saved_width; - view->swayc->height = view->swayc->saved_height; - view_autoconfigure(view); + view->swayc->width = view->swayc->saved_width; + view->swayc->height = view->swayc->saved_height; } } } @@ -508,8 +506,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->swayc = cont; view->border = config->border; view->border_thickness = config->border_thickness; - view->swayc->pending.border = config->border; - view->swayc->pending.border_thickness = config->border_thickness; view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -977,7 +973,7 @@ bool view_is_visible(struct sway_view *view) { } // Check the workspace is visible if (!is_sticky) { - return workspace_is_visible(workspace); + return workspace_is_visible(workspace); } return true; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index ad581a96..9ba210fd 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -60,12 +60,6 @@ struct sway_container *workspace_create(struct sway_container *output, workspace->prev_layout = L_NONE; workspace->layout = container_get_default_layout(output); - workspace->pending.swayc_x = workspace->x; - workspace->pending.swayc_y = workspace->y; - workspace->pending.swayc_width = workspace->width; - workspace->pending.swayc_height = workspace->height; - workspace->pending.layout = workspace->layout; - struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); if (!swayws) { return NULL; -- cgit v1.2.3-54-g00ecf From bb66e6d578fdc68fb33d0fde921390d74f20bb31 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Jun 2018 22:57:34 +1000 Subject: Refactor everything that needs to arrange windows * The arrange_foo functions are now replaced with arrange_and_commit, or with manually created transactions and arrange_windows x2. * The arrange functions are now only called from the highest level functions rather than from both high level and low level functions. * Due to the previous point, view_set_fullscreen_raw and view_set_fullscreen are both merged into one function again. * Floating and fullscreen are now working with transactions. --- include/sway/desktop/transaction.h | 7 +--- include/sway/tree/arrange.h | 7 ---- include/sway/tree/view.h | 5 +++ sway/commands/border.c | 7 ++-- sway/commands/floating.c | 3 ++ sway/commands/fullscreen.c | 4 +++ sway/commands/layout.c | 2 +- sway/commands/move.c | 41 +++++++++++++++++++--- sway/commands/reload.c | 2 +- sway/commands/resize.c | 2 +- sway/commands/split.c | 2 +- sway/commands/swap.c | 12 +++++++ sway/config.c | 4 +-- sway/desktop/layer_shell.c | 2 +- sway/desktop/output.c | 8 ++--- sway/desktop/transaction.c | 19 +++++++--- sway/desktop/xdg_shell.c | 18 ++++++---- sway/desktop/xdg_shell_v6.c | 20 +++++++---- sway/desktop/xwayland.c | 3 ++ sway/tree/arrange.c | 63 +++++++++++++++++---------------- sway/tree/container.c | 10 +++--- sway/tree/layout.c | 71 ++++++++++---------------------------- sway/tree/view.c | 49 +++++++++----------------- sway/tree/workspace.c | 2 +- 24 files changed, 192 insertions(+), 171 deletions(-) (limited to 'include') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 575d28c8..5aff28e9 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -16,12 +16,7 @@ * the updates all at the same time. */ -struct sway_transaction { - struct wl_event_source *timer; - list_t *instructions; // struct sway_transaction_instruction * - list_t *damage; // struct wlr_box * - size_t num_waiting; -}; +struct sway_transaction; /** * Create a new transaction. diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index 23cd66dc..897a9392 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -23,11 +23,4 @@ void arrange_windows(struct sway_container *container, */ void arrange_and_commit(struct sway_container *container); -// These functions are temporary and are only here to make everything compile. -// They are wrappers around arrange_and_commit. -void arrange_root(void); -void arrange_output(struct sway_container *container); -void arrange_workspace(struct sway_container *container); -void arrange_children_of(struct sway_container *container); - #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index f47db567..d0093db5 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -214,6 +214,11 @@ const char *view_get_shell(struct sway_view *view); uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); +/** + * Center the view in its workspace and build the swayc decorations around it. + */ +void view_init_floating(struct sway_view *view); + /** * Configure the view's position and size based on the swayc's position and * size, taking borders into consideration. diff --git a/sway/commands/border.c b/sway/commands/border.c index 0b059562..6db85395 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -38,13 +39,11 @@ struct cmd_results *cmd_border(int argc, char **argv) { } if (container_is_floating(view->swayc)) { - container_damage_whole(view->swayc); container_set_geometry_from_floating_view(view->swayc); - container_damage_whole(view->swayc); - } else { - view_autoconfigure(view); } + arrange_and_commit(view->swayc); + struct sway_seat *seat = input_manager_current_seat(input_manager); if (seat->cursor) { cursor_send_pointer_motion(seat->cursor, 0, false); diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 46b761da..e6003521 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -36,5 +36,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); + struct sway_container *workspace = container_parent(container, C_WORKSPACE); + arrange_and_commit(workspace); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index ec9ec276..1a4d8b41 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -1,6 +1,7 @@ #include "log.h" #include "sway/commands.h" #include "sway/config.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/layout.h" @@ -32,5 +33,8 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { view_set_fullscreen(view, wants_fullscreen); + struct sway_container *workspace = container_parent(container, C_WORKSPACE); + arrange_and_commit(workspace->parent); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index a009e38f..9945fa5c 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { } container_notify_subtree_changed(parent); - arrange_children_of(parent); + arrange_and_commit(parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index dc9a6f6f..2c9fb77a 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -5,8 +5,10 @@ #include #include #include "sway/commands.h" +#include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/workspace.h" @@ -96,6 +98,12 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, seat_set_focus(config->handler_context.seat, focus); container_reap_empty(old_parent); container_reap_empty(destination->parent); + + struct sway_transaction *txn = transaction_create(); + arrange_windows(old_parent, txn); + arrange_windows(destination->parent, txn); + transaction_commit(txn); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) { @@ -125,6 +133,12 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, seat_set_focus(config->handler_context.seat, old_parent); container_reap_empty(old_parent); container_reap_empty(focus->parent); + + struct sway_transaction *txn = transaction_create(); + arrange_windows(old_parent, txn); + arrange_windows(focus->parent, txn); + transaction_commit(txn); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } return cmd_results_new(CMD_INVALID, "move", expected_syntax); @@ -152,9 +166,28 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, current = container_parent(current, C_WORKSPACE); } container_move_to(current, destination); + + struct sway_transaction *txn = transaction_create(); + arrange_windows(source, txn); + arrange_windows(destination, txn); + transaction_commit(txn); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static void move_in_direction(struct sway_container *container, + enum wlr_direction direction, int move_amt) { + struct sway_container *old_parent = container->parent; + container_move(container, direction, move_amt); + + struct sway_transaction *txn = transaction_create(); + arrange_windows(old_parent, txn); + if (container->parent != old_parent) { + arrange_windows(container->parent, txn); + } + transaction_commit(txn); +} + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; int move_amt = 10; @@ -173,13 +206,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { } if (strcasecmp(argv[0], "left") == 0) { - container_move(current, MOVE_LEFT, move_amt); + move_in_direction(current, MOVE_LEFT, move_amt); } else if (strcasecmp(argv[0], "right") == 0) { - container_move(current, MOVE_RIGHT, move_amt); + move_in_direction(current, MOVE_RIGHT, move_amt); } else if (strcasecmp(argv[0], "up") == 0) { - container_move(current, MOVE_UP, move_amt); + move_in_direction(current, MOVE_UP, move_amt); } else if (strcasecmp(argv[0], "down") == 0) { - container_move(current, MOVE_DOWN, move_amt); + move_in_direction(current, MOVE_DOWN, move_amt); } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { return cmd_move_container(current, argc, argv); diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 092dd46f..9fc213c4 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -12,6 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) { } load_swaybars(); - arrange_root(); + arrange_and_commit(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 29637953..6357343e 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -182,7 +182,7 @@ static void resize_tiled(int amount, enum resize_axis axis) { } } - arrange_children_of(parent->parent); + arrange_and_commit(parent->parent); } static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { diff --git a/sway/commands/split.c b/sway/commands/split.c index 57e42a5a..7ea14953 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) { } struct sway_container *parent = container_split(con, layout); container_create_notify(parent); - arrange_children_of(parent); + arrange_and_commit(parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e8dfc57f..e052058f 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,6 +1,8 @@ #include #include #include "sway/commands.h" +#include "sway/desktop/transaction.h" +#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" #include "stringop.h" @@ -76,5 +78,15 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } container_swap(current, other); + + struct sway_transaction *txn = transaction_create(); + arrange_windows(current->parent, txn); + + if (other->parent != current->parent) { + arrange_windows(other->parent, txn); + } + + transaction_commit(txn); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/config.c b/sway/config.c index 949c5cd3..12a02163 100644 --- a/sway/config.c +++ b/sway/config.c @@ -531,7 +531,7 @@ static int detect_brace_on_following_line(FILE *file, char *line, } while (peeked && strlen(peeked) == 0); if (peeked && strlen(peeked) == 1 && peeked[0] == '{') { - fseek(file, position, SEEK_SET); + fseek(file, position, SEEK_SET); } else { lines = 0; } @@ -735,6 +735,6 @@ void config_update_font_height(bool recalculate) { } if (config->font_height != prev_max_height) { - arrange_root(); + arrange_and_commit(&root_container); } } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 3accdefb..fe5fc316 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -176,7 +176,7 @@ void arrange_layers(struct sway_output *output) { sizeof(struct wlr_box)) != 0) { wlr_log(L_DEBUG, "Usable area changed, rearranging output"); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - arrange_output(output->swayc); + arrange_and_commit(output->swayc); } // Arrange non-exlusive surfaces from top->bottom diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8af05bc3..29efdd50 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1238,13 +1238,13 @@ static void handle_destroy(struct wl_listener *listener, void *data) { static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); arrange_layers(output); - arrange_output(output->swayc); + arrange_and_commit(output->swayc); } static void handle_transform(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, transform); arrange_layers(output); - arrange_output(output->swayc); + arrange_and_commit(output->swayc); } static void handle_scale_iterator(struct sway_container *view, void *data) { @@ -1254,8 +1254,8 @@ static void handle_scale_iterator(struct sway_container *view, void *data) { static void handle_scale(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, scale); arrange_layers(output); - arrange_output(output->swayc); container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); + arrange_and_commit(output->swayc); } void handle_new_output(struct wl_listener *listener, void *data) { @@ -1314,5 +1314,5 @@ void output_enable(struct sway_output *output) { output->damage_destroy.notify = damage_handle_destroy; arrange_layers(output); - arrange_root(); + arrange_and_commit(&root_container); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 313e707b..ee9883e2 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -17,6 +17,13 @@ */ #define TIMEOUT_MS 200 +struct sway_transaction { + struct wl_event_source *timer; + list_t *instructions; // struct sway_transaction_instruction * + list_t *damage; // struct wlr_box * + size_t num_waiting; +}; + struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_container *container; @@ -162,16 +169,18 @@ void transaction_commit(struct sway_transaction *transaction) { for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - if (instruction->container->type == C_VIEW) { - struct sway_view *view = instruction->container->sway_view; - instruction->serial = view_configure(view, + struct sway_container *con = instruction->container; + if (con->type == C_VIEW && + (con->current.view_width != instruction->state.view_width || + con->current.view_height != instruction->state.view_height)) { + instruction->serial = view_configure(con->sway_view, instruction->state.view_x, instruction->state.view_y, instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_texture(view); - list_add(view->instructions, instruction); + save_view_texture(con->sway_view); + list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index f43a0a1b..d22c967c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -5,10 +5,10 @@ #include #include #include "log.h" -#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" @@ -210,8 +210,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = view->wlr_xdg_surface->surface->current->width; view->natural_height = view->wlr_xdg_surface->surface->current->height; } + view_map(view, view->wlr_xdg_surface->surface); + if (xdg_surface->toplevel->client_pending.fullscreen) { + view_set_fullscreen(view, true); + } + arrange_and_commit(view->swayc->parent); + xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, &xdg_shell_view->commit); @@ -219,10 +225,6 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->new_popup.notify = handle_new_popup; wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup); - - if (xdg_surface->toplevel->client_pending.fullscreen) { - view_set_fullscreen(view, true); - } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -237,6 +239,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_xdg_toplevel_set_fullscreen_event *e = data; struct wlr_xdg_surface *xdg_surface = xdg_shell_view->view.wlr_xdg_surface; + struct sway_view *view = &xdg_shell_view->view; if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, "xdg_shell requested fullscreen of surface with role %i", @@ -247,7 +250,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - view_set_fullscreen(&xdg_shell_view->view, e->fullscreen); + view_set_fullscreen(view, e->fullscreen); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); } void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index bce59174..7ec9e6cb 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -3,10 +3,10 @@ #include #include #include -#include "sway/desktop/transaction.h" +#include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" -#include "sway/server.h" #include "sway/tree/view.h" #include "sway/input/seat.h" #include "sway/input/input-manager.h" @@ -210,8 +210,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; } + view_map(view, view->wlr_xdg_surface_v6->surface); + if (xdg_surface->toplevel->client_pending.fullscreen) { + view_set_fullscreen(view, true); + } + arrange_and_commit(view->swayc->parent); + xdg_shell_v6_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, &xdg_shell_v6_view->commit); @@ -219,10 +225,6 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->new_popup.notify = handle_new_popup; wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_v6_view->new_popup); - - if (xdg_surface->toplevel->client_pending.fullscreen) { - view_set_fullscreen(view, true); - } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -237,6 +239,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; struct wlr_xdg_surface_v6 *xdg_surface = xdg_shell_v6_view->view.wlr_xdg_surface_v6; + struct sway_view *view = &xdg_shell_v6_view->view; if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, "xdg_shell_v6 requested fullscreen of surface with role %i", @@ -247,7 +250,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - view_set_fullscreen(&xdg_shell_v6_view->view, e->fullscreen); + view_set_fullscreen(view, e->fullscreen); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index d8442530..70929d48 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -11,6 +11,7 @@ #include "sway/input/seat.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" @@ -292,6 +293,7 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); } + arrange_and_commit(view->swayc); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -325,6 +327,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } view_set_fullscreen(view, xsurface->fullscreen); + arrange_and_commit(view->swayc); } static void handle_set_title(struct wl_listener *listener, void *data) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index cf7ce61c..e138410d 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -138,7 +138,23 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { } } -static void _arrange_children_of(struct sway_container *parent, +static void arrange_children_of(struct sway_container *parent, + struct sway_transaction *transaction); + +static void arrange_floating(struct sway_container *floating, + struct sway_transaction *transaction) { + for (int i = 0; i < floating->children->length; ++i) { + struct sway_container *floater = floating->children->items[i]; + if (floater->type == C_VIEW) { + view_autoconfigure(floater->sway_view); + } else { + arrange_children_of(floater, transaction); + } + transaction_add_container(transaction, floater); + } +} + +static void arrange_children_of(struct sway_container *parent, struct sway_transaction *transaction) { if (config->reloading) { return; @@ -162,7 +178,8 @@ static void _arrange_children_of(struct sway_container *parent, apply_horiz_layout(parent); break; case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); + arrange_floating(parent, transaction); + break; } // Recurse into child containers @@ -171,13 +188,13 @@ static void _arrange_children_of(struct sway_container *parent, if (child->type == C_VIEW) { view_autoconfigure(child->sway_view); } else { - _arrange_children_of(child, transaction); + arrange_children_of(child, transaction); } transaction_add_container(transaction, child); } } -static void _arrange_workspace(struct sway_container *workspace, +static void arrange_workspace(struct sway_container *workspace, struct sway_transaction *transaction) { if (config->reloading) { return; @@ -193,10 +210,11 @@ static void _arrange_workspace(struct sway_container *workspace, transaction_add_container(transaction, workspace); wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, workspace->x, workspace->y); - _arrange_children_of(workspace, transaction); + arrange_floating(workspace->sway_workspace->floating, transaction); + arrange_children_of(workspace, transaction); } -static void _arrange_output(struct sway_container *output, +static void arrange_output(struct sway_container *output, struct sway_transaction *transaction) { if (config->reloading) { return; @@ -213,11 +231,11 @@ static void _arrange_output(struct sway_container *output, output->name, output->x, output->y); for (int i = 0; i < output->children->length; ++i) { struct sway_container *workspace = output->children->items[i]; - _arrange_workspace(workspace, transaction); + arrange_workspace(workspace, transaction); } } -static void _arrange_root(struct sway_transaction *transaction) { +static void arrange_root(struct sway_transaction *transaction) { if (config->reloading) { return; } @@ -232,7 +250,7 @@ static void _arrange_root(struct sway_transaction *transaction) { transaction_add_container(transaction, &root_container); for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; - _arrange_output(output, transaction); + arrange_output(output, transaction); } } @@ -240,19 +258,21 @@ void arrange_windows(struct sway_container *container, struct sway_transaction *transaction) { switch (container->type) { case C_ROOT: - _arrange_root(transaction); + arrange_root(transaction); break; case C_OUTPUT: - _arrange_output(container, transaction); + arrange_output(container, transaction); break; case C_WORKSPACE: - _arrange_workspace(container, transaction); + arrange_workspace(container, transaction); break; case C_CONTAINER: - _arrange_children_of(container, transaction); + arrange_children_of(container, transaction); transaction_add_container(transaction, container); break; case C_VIEW: + view_autoconfigure(container->sway_view); + transaction_add_container(transaction, container); break; case C_TYPES: break; @@ -265,20 +285,3 @@ void arrange_and_commit(struct sway_container *container) { arrange_windows(container, transaction); transaction_commit(transaction); } - -// These functions are only temporary -void arrange_root() { - arrange_and_commit(&root_container); -} - -void arrange_output(struct sway_container *container) { - arrange_and_commit(container); -} - -void arrange_workspace(struct sway_container *container) { - arrange_and_commit(container); -} - -void arrange_children_of(struct sway_container *container) { - arrange_and_commit(container); -} diff --git a/sway/tree/container.c b/sway/tree/container.c index e6956f5c..d312eb60 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -204,6 +204,7 @@ static struct sway_container *container_workspace_destroy( container_move_to(floating->children->items[i], new_workspace->sway_workspace->floating); } + arrange_and_commit(new_workspace); } struct sway_workspace *sway_workspace = workspace->sway_workspace; @@ -264,10 +265,10 @@ static struct sway_container *container_output_destroy( } container_sort_workspaces(new_output); - arrange_output(new_output); } } } + arrange_and_commit(&root_container); wl_list_remove(&output->sway_output->mode.link); wl_list_remove(&output->sway_output->transform.link); @@ -924,13 +925,12 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *workspace = container_parent(container, C_WORKSPACE); struct sway_seat *seat = input_manager_current_seat(input_manager); - container_damage_whole(container); if (enable) { container_remove_child(container); container_add_child(workspace->sway_workspace->floating, container); if (container->type == C_VIEW) { - view_autoconfigure(container->sway_view); + view_init_floating(container->sway_view); } seat_set_focus(seat, seat_get_focus_inactive(seat, container)); container_reap_empty_recursive(workspace); @@ -943,8 +943,8 @@ void container_set_floating(struct sway_container *container, bool enable) { container->is_sticky = false; container_reap_empty_recursive(workspace->sway_workspace->floating); } - arrange_workspace(workspace); - container_damage_whole(container); + + ipc_event_window(container, "floating"); } void container_set_geometry_from_floating_view(struct sway_container *con) { diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 6d4cd088..65b61495 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -22,7 +22,7 @@ struct sway_container root_container; static void output_layout_handle_change(struct wl_listener *listener, void *data) { - arrange_root(); + arrange_and_commit(&root_container); } void layout_init(void) { @@ -56,18 +56,17 @@ static int index_child(const struct sway_container *child) { return -1; } -static void container_handle_fullscreen_reparent(struct sway_container *viewcon, +static void container_handle_fullscreen_reparent(struct sway_container *con, struct sway_container *old_parent) { - if (viewcon->type != C_VIEW || !viewcon->sway_view->is_fullscreen) { + if (con->type != C_VIEW || !con->sway_view->is_fullscreen) { return; } - struct sway_view *view = viewcon->sway_view; + struct sway_view *view = con->sway_view; struct sway_container *old_workspace = old_parent; if (old_workspace && old_workspace->type != C_WORKSPACE) { old_workspace = container_parent(old_workspace, C_WORKSPACE); } - struct sway_container *new_workspace = container_parent(view->swayc, - C_WORKSPACE); + struct sway_container *new_workspace = container_parent(con, C_WORKSPACE); if (old_workspace == new_workspace) { return; } @@ -78,15 +77,19 @@ static void container_handle_fullscreen_reparent(struct sway_container *viewcon, // Mark the new workspace as fullscreen if (new_workspace->sway_workspace->fullscreen) { - view_set_fullscreen_raw( - new_workspace->sway_workspace->fullscreen, false); + view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false); } new_workspace->sway_workspace->fullscreen = view; // Resize view to new output dimensions struct sway_container *output = new_workspace->parent; - view_configure(view, 0, 0, output->width, output->height); - view->swayc->width = output->width; - view->swayc->height = output->height; + view->x = output->x; + view->y = output->y; + view->width = output->width; + view->height = output->height; + con->x = output->x; + con->y = output->y; + con->width = output->width; + con->height = output->height; } void container_insert_child(struct sway_container *parent, @@ -188,18 +191,7 @@ void container_move_to(struct sway_container *container, } container_notify_subtree_changed(old_parent); container_notify_subtree_changed(new_parent); - if (old_parent) { - if (old_parent->type == C_OUTPUT) { - arrange_output(old_parent); - } else { - arrange_children_of(old_parent); - } - } - if (new_parent->type == C_OUTPUT) { - arrange_output(new_parent); - } else { - arrange_children_of(new_parent); - } + // If view was moved to a fullscreen workspace, refocus the fullscreen view struct sway_container *new_workspace = container; if (new_workspace->type != C_WORKSPACE) { @@ -214,7 +206,8 @@ void container_move_to(struct sway_container *container, if (focus_ws->type != C_WORKSPACE) { focus_ws = container_parent(focus_ws, C_WORKSPACE); } - seat_set_focus(seat, new_workspace->sway_workspace->fullscreen->swayc); + seat_set_focus(seat, + new_workspace->sway_workspace->fullscreen->swayc); if (focus_ws != new_workspace) { seat_set_focus(seat, focus); } @@ -308,7 +301,6 @@ static void workspace_rejigger(struct sway_container *ws, container_reap_empty_recursive(original_parent); wl_signal_emit(&child->events.reparent, original_parent); container_create_notify(new_parent); - arrange_workspace(ws); } static void move_out_of_tabs_stacks(struct sway_container *container, @@ -319,11 +311,6 @@ static void move_out_of_tabs_stacks(struct sway_container *container, wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); current->parent->layout = move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; - if (current->parent->type == C_WORKSPACE) { - arrange_workspace(current->parent); - } else { - arrange_children_of(current->parent); - } return; } @@ -339,11 +326,6 @@ static void move_out_of_tabs_stacks(struct sway_container *container, container_flatten(new_parent->parent); } container_create_notify(new_parent); - if (is_workspace) { - arrange_workspace(new_parent->parent); - } else { - arrange_children_of(new_parent); - } container_notify_subtree_changed(new_parent); } @@ -367,10 +349,7 @@ void container_move(struct sway_container *container, struct sway_container *new_parent = container_flatten(parent); if (new_parent != parent) { - // Special case: we were the last one in this container, so flatten it - // and leave - arrange_children_of(new_parent); - update_debug_tree(); + // Special case: we were the last one in this container, so leave return; } @@ -452,12 +431,9 @@ void container_move(struct sway_container *container, wlr_log(L_DEBUG, "Hit limit, " "promoting descendant to sibling"); // Special case - struct sway_container *old_parent = container->parent; container_insert_child(current->parent, container, index + (offs < 0 ? 0 : 1)); container->width = container->height = 0; - arrange_children_of(current->parent); - arrange_children_of(old_parent); return; } } else { @@ -491,14 +467,11 @@ void container_move(struct sway_container *container, wlr_log(L_DEBUG, "Swapping siblings"); sibling->parent->children->items[index + offs] = container; sibling->parent->children->items[index] = sibling; - arrange_children_of(sibling->parent); } else { wlr_log(L_DEBUG, "Promoting to sibling of cousin"); container_insert_child(sibling->parent, container, index_child(sibling) + (offs > 0 ? 0 : 1)); container->width = container->height = 0; - arrange_children_of(sibling->parent); - arrange_children_of(old_parent); } sibling = NULL; break; @@ -512,8 +485,6 @@ void container_move(struct sway_container *container, "(move dir: %d)", limit, move_dir); container_insert_child(sibling, container, limit); container->width = container->height = 0; - arrange_children_of(sibling); - arrange_children_of(old_parent); sibling = NULL; } else { wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); @@ -537,8 +508,6 @@ void container_move(struct sway_container *container, container_add_child(sibling, container); } container->width = container->height = 0; - arrange_children_of(sibling); - arrange_children_of(old_parent); sibling = NULL; } break; @@ -863,7 +832,6 @@ struct sway_container *container_split(struct sway_container *child, // Special case: this just behaves like splitt child->prev_layout = child->layout; child->layout = layout; - arrange_children_of(child); return child; } @@ -1044,9 +1012,6 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { prev_workspace_name = stored_prev_name; } - arrange_children_of(con1->parent); - arrange_children_of(con2->parent); - if (fs1 && con2->type == C_VIEW) { view_set_fullscreen(con2->sway_view, true); } diff --git a/sway/tree/view.c b/sway/tree/view.c index dbf803c6..658a94e8 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -135,22 +135,22 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, return 0; } -static void view_autoconfigure_floating(struct sway_view *view) { +void view_init_floating(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); int max_width = ws->width * 0.6666; int max_height = ws->height * 0.6666; - int width = + view->width = view->natural_width > max_width ? max_width : view->natural_width; - int height = + view->height = view->natural_height > max_height ? max_height : view->natural_height; - int lx = ws->x + (ws->width - width) / 2; - int ly = ws->y + (ws->height - height) / 2; + view->x = ws->x + (ws->width - view->width) / 2; + view->y = ws->y + (ws->height - view->height) / 2; // If the view's border is B_NONE then these properties are ignored. view->border_top = view->border_bottom = true; view->border_left = view->border_right = true; - view_configure(view, lx, ly, width, height); + container_set_geometry_from_floating_view(view->swayc); } void view_autoconfigure(struct sway_view *view) { @@ -162,12 +162,14 @@ void view_autoconfigure(struct sway_view *view) { struct sway_container *output = container_parent(view->swayc, C_OUTPUT); if (view->is_fullscreen) { - view_configure(view, output->x, output->y, output->width, output->height); + view->x = output->x; + view->y = output->y; + view->width = output->width; + view->height = output->height; return; } if (container_is_floating(view->swayc)) { - view_autoconfigure_floating(view); return; } @@ -268,8 +270,7 @@ void view_set_activated(struct sway_view *view, bool activated) { } } -// Set fullscreen, but without IPC events or arranging windows. -void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { +void view_set_fullscreen(struct sway_view *view, bool fullscreen) { if (view->is_fullscreen == fullscreen) { return; } @@ -315,26 +316,17 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { } else { workspace->sway_workspace->fullscreen = NULL; if (container_is_floating(view->swayc)) { - view_configure(view, view->saved_x, view->saved_y, - view->saved_width, view->saved_height); + view->x = view->saved_x; + view->y = view->saved_y; + view->width = view->saved_width; + view->height = view->saved_height; + container_set_geometry_from_floating_view(view->swayc); } else { view->swayc->width = view->swayc->saved_width; view->swayc->height = view->swayc->saved_height; } } -} - -void view_set_fullscreen(struct sway_view *view, bool fullscreen) { - if (view->is_fullscreen == fullscreen) { - return; - } - view_set_fullscreen_raw(view, fullscreen); - - struct sway_container *workspace = - container_parent(view->swayc, C_WORKSPACE); - arrange_workspace(workspace); - output_damage_whole(workspace->parent->sway_output); ipc_event_window(view->swayc, "fullscreen_mode"); } @@ -517,8 +509,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (view->impl->wants_floating && view->impl->wants_floating(view)) { container_set_floating(view->swayc, true); - } else { - arrange_children_of(cont->parent); } input_manager_set_focus(input_manager, cont); @@ -530,7 +520,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { container_notify_subtree_changed(view->swayc->parent); view_execute_criteria(view); - container_damage_whole(cont); view_handle_container_reparent(&view->container_reparent, NULL); } @@ -561,11 +550,7 @@ void view_unmap(struct sway_view *view) { view->title_format = NULL; } - if (parent->type == C_OUTPUT) { - arrange_output(parent); - } else { - arrange_children_of(parent); - } + arrange_and_commit(parent); } void view_update_position(struct sway_view *view, double lx, double ly) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 9ba210fd..ead752ad 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -425,7 +425,7 @@ bool workspace_switch(struct sway_container *workspace) { } seat_set_focus(seat, next); struct sway_container *output = container_parent(workspace, C_OUTPUT); - arrange_output(output); + arrange_and_commit(output); return true; } -- cgit v1.2.3-54-g00ecf From 1c89f32533534f6e78c81c95578f40df45bb9016 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Jun 2018 20:42:12 +1000 Subject: Preserve buffers during transactions * Also fix parts of the rendering where it was rendering the pending state instead of current. --- include/sway/tree/view.h | 9 ++-- sway/desktop/output.c | 101 +++++++++++++++++++++++---------------------- sway/desktop/transaction.c | 28 ++++++++----- sway/desktop/xwayland.c | 2 +- sway/tree/container.c | 3 -- 5 files changed, 76 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index d0093db5..fc4c8df9 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -72,10 +72,11 @@ struct sway_view { list_t *marks; // char * list_t *instructions; // struct sway_transaction_instruction * - // If saved_texture is set, the main surface of the view will render this - // texture instead of its own. This is used while waiting for transactions - // to complete. - struct wlr_texture *saved_texture; + // If saved_buffer is set, the main surface of the view will render this + // buffer/texture instead of its own. This is used while waiting for + // transactions to complete. + struct wlr_buffer *saved_buffer; + int saved_surface_width, saved_surface_height; struct wlr_texture *marks_focused; struct wlr_texture *marks_focused_inactive; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 37528cac..a485cb10 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -112,8 +113,8 @@ static bool get_surface_box(struct root_geometry *geo, static bool get_view_box(struct root_geometry *geo, struct sway_output *output, struct sway_view *view, int sx, int sy, struct wlr_box *surface_box) { - int sw = view->width; - int sh = view->height; + int sw = view->saved_surface_width; + int sh = view->saved_surface_height; double _sx = sx, _sy = sy; rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, @@ -157,10 +158,10 @@ static void output_view_for_each_surface(struct sway_view *view, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct render_data *data = user_data; - geo->x = view->x - data->output->swayc->x; - geo->y = view->y - data->output->swayc->y; - geo->width = view->surface->current->width; - geo->height = view->surface->current->height; + geo->x = view->swayc->current.view_x - data->output->swayc->x; + geo->y = view->swayc->current.view_y - data->output->swayc->y; + geo->width = view->swayc->current.view_width; + geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO view_for_each_surface(view, iterator, user_data); @@ -277,11 +278,11 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, struct wlr_box box; bool intersects; - // If this is the main surface of a view, render the saved_texture instead + // If this is the main surface of a view, render the saved_buffer instead // if it exists. It exists when we are mid-transaction. - if (data->view && data->view->saved_texture && + if (data->view && data->view->saved_buffer && data->view->surface == surface) { - texture = data->view->saved_texture; + texture = data->view->saved_buffer->texture; intersects = get_view_box(&data->root_geo, data->output, data->view, sx, sy, &box); } else { @@ -405,46 +406,46 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, float output_scale = output->wlr_output->scale; float color[4]; - if (view->border != B_NONE) { - if (view->border_left) { + if (con->current.border != B_NONE) { + if (con->current.border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = view->y; - box.width = view->border_thickness; - box.height = view->height; + box.x = con->current.swayc_x; + box.y = con->current.view_y; + box.width = con->current.border_thickness; + box.height = con->current.view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (view->border_right) { + if (con->current.border_right) { if (con->parent->children->length == 1 - && con->parent->layout == L_HORIZ) { + && con->parent->current.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 = view->x + view->width; - box.y = view->y; - box.width = view->border_thickness; - box.height = view->height; + box.x = con->current.view_x + con->current.view_width; + box.y = con->current.view_y; + box.width = con->current.border_thickness; + box.height = con->current.view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (view->border_bottom) { + if (con->current.border_bottom) { if (con->parent->children->length == 1 - && con->parent->layout == L_VERT) { + && con->parent->current.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 = con->x; - box.y = view->y + view->height; - box.width = con->width; - box.height = view->border_thickness; + box.x = con->current.swayc_x; + box.y = con->current.view_y + con->current.view_height; + box.width = con->current.swayc_width; + box.height = con->current.border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -468,9 +469,8 @@ static void render_titlebar(struct sway_output *output, struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; - struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL; float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = con->parent->layout; + enum sway_container_layout layout = con->parent->current.layout; bool is_last_child = con->parent->children->items[con->parent->children->length - 1] == con; @@ -489,9 +489,11 @@ static void render_titlebar(struct sway_output *output, bool connects_sides = false; if (layout == L_HORIZ || layout == L_VERT || (layout == L_STACKED && is_last_child)) { - if (view) { - left_offset = view->border_left * view->border_thickness; - right_offset = view->border_right * view->border_thickness; + if (con->type == C_VIEW) { + left_offset = + con->current.border_left * con->current.border_thickness; + right_offset = + con->current.border_right * con->current.border_thickness; connects_sides = true; } } @@ -612,15 +614,16 @@ static void render_titlebar(struct sway_output *output, // Left pixel in line with bottom bar box.x = x; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = view->border_thickness * view->border_left; + box.width = con->current.border_thickness * con->current.border_left; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Right pixel in line with bottom bar - box.x = x + width - view->border_thickness * view->border_right; + box.x = x + width - + con->current.border_thickness * con->current.border_right; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = view->border_thickness * view->border_right; + box.width = con->current.border_thickness * con->current.border_right; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -633,8 +636,7 @@ static void render_titlebar(struct sway_output *output, static void render_top_border(struct sway_output *output, pixman_region32_t *output_damage, struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->sway_view; - if (!view->border_top) { + if (!con->current.border_top) { return; } struct wlr_box box; @@ -644,10 +646,10 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y; - box.width = con->width; - box.height = view->border_thickness; + box.x = con->current.swayc_x; + box.y = con->current.swayc_y; + box.width = con->current.swayc_width; + box.height = con->current.border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } @@ -688,9 +690,10 @@ static void render_container_simple(struct sway_output *output, marks_texture = child->sway_view->marks_unfocused; } - if (child->sway_view->border == B_NORMAL) { - render_titlebar(output, damage, child, child->x, child->y, - child->width, colors, title_texture, marks_texture); + if (child->current.border == B_NORMAL) { + render_titlebar(output, damage, child, child->current.swayc_x, + child->current.swayc_y, child->current.swayc_width, + colors, title_texture, marks_texture); } else { render_top_border(output, damage, child, colors); } @@ -739,15 +742,15 @@ static void render_container_tabbed(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int tab_width = con->width / con->children->length; - int x = con->x + tab_width * i; + int tab_width = con->current.swayc_width / con->children->length; + int x = con->current.swayc_x + tab_width * i; // Make last tab use the remaining width of the parent if (i == con->children->length - 1) { - tab_width = con->width - tab_width * i; + tab_width = con->current.swayc_width - tab_width * i; } - render_titlebar(output, damage, child, x, child->y, tab_width, colors, - title_texture, marks_texture); + render_titlebar(output, damage, child, x, child->current.swayc_y, + tab_width, colors, title_texture, marks_texture); if (child == current) { current_colors = colors; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 07bfbf7a..77377a18 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "sway/debug.h" #include "sway/desktop/transaction.h" @@ -112,16 +113,23 @@ void transaction_add_damage(struct sway_transaction *transaction, list_add(transaction->damage, box); } -static void save_view_texture(struct sway_view *view) { - wlr_texture_destroy(view->saved_texture); - view->saved_texture = NULL; - - // TODO: Copy the texture and store it in view->saved_texture. +static void save_view_buffer(struct sway_view *view) { + if (view->saved_buffer) { + wlr_buffer_unref(view->saved_buffer); + } + wlr_buffer_ref(view->surface->buffer); + view->saved_buffer = view->surface->buffer; + view->saved_surface_width = view->surface->current->width; + view->saved_surface_height = view->surface->current->height; } -static void remove_saved_view_texture(struct sway_view *view) { - wlr_texture_destroy(view->saved_texture); - view->saved_texture = NULL; +static void remove_saved_view_buffer(struct sway_view *view) { + if (view->saved_buffer) { + wlr_buffer_unref(view->saved_buffer); + view->saved_buffer = NULL; + view->saved_surface_width = 0; + view->saved_surface_height = 0; + } } /** @@ -141,7 +149,7 @@ static void transaction_apply(struct sway_transaction *transaction) { sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - remove_saved_view_texture(container->sway_view); + remove_saved_view_buffer(container->sway_view); } } @@ -183,7 +191,7 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_texture(con->sway_view); + save_view_buffer(con->sway_view); list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 70929d48..55917bf6 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -261,8 +261,8 @@ static void handle_commit(struct wl_listener *listener, void *data) { view_update_size(view, view->swayc->width, view->swayc->height); } view_update_position(view, view->x, view->y); - view_damage_from(view); } + view_damage_from(view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index f8620b72..b071f394 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -766,9 +766,6 @@ static void update_title_texture(struct sway_container *con, "Unexpected type %s", container_type_to_str(con->type))) { return; } - if (!con->width) { - return; - } struct sway_container *output = container_parent(con, C_OUTPUT); if (!output) { return; -- cgit v1.2.3-54-g00ecf From 38398e2d77d57dc06b67ec88a54091c897915602 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 23 Jun 2018 16:24:11 +1000 Subject: Implement atomic layout updates for tree operations This implements atomic layout updates for when views map, reparent or unmap. --- include/sway/desktop/transaction.h | 9 ++ include/sway/server.h | 7 + include/sway/tree/container.h | 16 +- include/sway/tree/view.h | 18 +-- sway/commands/split.c | 2 +- sway/desktop/output.c | 293 ++++++++++++++++++------------------- sway/desktop/transaction.c | 176 ++++++++++++++-------- sway/desktop/xdg_shell.c | 35 +++-- sway/desktop/xdg_shell_v6.c | 30 ++-- sway/desktop/xwayland.c | 45 ++++-- sway/main.c | 1 + sway/server.c | 4 + sway/tree/arrange.c | 25 +++- sway/tree/container.c | 184 +++++++++++------------ sway/tree/layout.c | 2 + sway/tree/output.c | 2 - sway/tree/view.c | 76 +++++----- sway/tree/workspace.c | 3 + 18 files changed, 542 insertions(+), 386 deletions(-) (limited to 'include') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 5aff28e9..d6adc609 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -1,5 +1,6 @@ #ifndef _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H +#include #include "sway/tree/container.h" /** @@ -48,4 +49,12 @@ void transaction_commit(struct sway_transaction *transaction); */ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); +/** + * Get the texture that should be rendered for a view. + * + * In most cases this will return the normal live texture for a view, but if the + * view is in a transaction then it'll return a saved texture. + */ +struct wlr_texture *transaction_get_texture(struct sway_view *view); + #endif diff --git a/include/sway/server.h b/include/sway/server.h index 65d96e7a..f5f88a5a 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -12,6 +12,7 @@ #include // TODO WLR: make Xwayland optional #include +#include "list.h" struct sway_server { struct wl_display *wl_display; @@ -43,6 +44,12 @@ struct sway_server { struct wlr_wl_shell *wl_shell; struct wl_listener wl_shell_surface; + + bool terminating; + + // When a view is being destroyed and is waiting for a transaction to + // complete it will be stored here. + list_t *destroying_containers; }; struct sway_server server; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index f4e978ea..7e78cbef 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -65,8 +65,8 @@ struct sway_container_state { double gaps_inner; double gaps_outer; - //struct sway_container *parent; - //list_t *children; + struct sway_container *parent; + list_t *children; // View properties double view_x, view_y; @@ -79,6 +79,10 @@ struct sway_container_state { bool border_bottom; bool border_left; bool border_right; + + // Workspace properties + struct sway_view *ws_fullscreen; + struct sway_container *ws_floating; }; struct sway_container { @@ -128,8 +132,6 @@ struct sway_container { struct sway_container *parent; - list_t *marks; // list of char* - float alpha; struct wlr_texture *title_focused; @@ -138,6 +140,10 @@ struct sway_container { struct wlr_texture *title_urgent; size_t title_height; + list_t *instructions; // struct sway_transaction_instruction * + + bool destroying; + struct { struct wl_signal destroy; // Raised after the tree updates, but before arrange_windows @@ -181,6 +187,8 @@ struct sway_container *workspace_create(struct sway_container *output, struct sway_container *container_view_create( struct sway_container *sibling, struct sway_view *sway_view); +void container_free(struct sway_container *cont); + struct sway_container *container_destroy(struct sway_container *container); struct sway_container *container_close(struct sway_container *container); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index fc4c8df9..5a615b43 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -37,7 +37,7 @@ struct sway_view_impl { void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); - void (*destroy)(struct sway_view *view); + void (*free)(struct sway_view *view); }; struct sway_view { @@ -68,15 +68,10 @@ struct sway_view { bool border_left; bool border_right; + bool destroying; + list_t *executed_criteria; // struct criteria * list_t *marks; // char * - list_t *instructions; // struct sway_transaction_instruction * - - // If saved_buffer is set, the main surface of the view will render this - // buffer/texture instead of its own. This is used while waiting for - // transactions to complete. - struct wlr_buffer *saved_buffer; - int saved_surface_width, saved_surface_height; struct wlr_texture *marks_focused; struct wlr_texture *marks_focused_inactive; @@ -244,11 +239,16 @@ void view_for_each_surface(struct sway_view *view, void view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl); +void view_free(struct sway_view *view); + void view_destroy(struct sway_view *view); void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); -void view_unmap(struct sway_view *view); +/** + * Unmap the view and return the surviving parent (after reaping). + */ +struct sway_container *view_unmap(struct sway_view *view); void view_update_position(struct sway_view *view, double lx, double ly); diff --git a/sway/commands/split.c b/sway/commands/split.c index 7ea14953..c40f4d9f 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) { } struct sway_container *parent = container_split(con, layout); container_create_notify(parent); - arrange_and_commit(parent); + arrange_and_commit(parent->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a485cb10..9db95ef5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -102,40 +102,8 @@ static bool get_surface_box(struct root_geometry *geo, wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); struct wlr_box output_box = { - .width = output->swayc->width, - .height = output->swayc->height, - }; - - struct wlr_box intersection; - return wlr_box_intersection(&output_box, &rotated_box, &intersection); -} - -static bool get_view_box(struct root_geometry *geo, - struct sway_output *output, struct sway_view *view, int sx, int sy, - struct wlr_box *surface_box) { - int sw = view->saved_surface_width; - int sh = view->saved_surface_height; - - double _sx = sx, _sy = sy; - rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, - geo->rotation); - - struct wlr_box box = { - .x = geo->x + _sx, - .y = geo->y + _sy, - .width = sw, - .height = sh, - }; - if (surface_box != NULL) { - memcpy(surface_box, &box, sizeof(struct wlr_box)); - } - - struct wlr_box rotated_box; - wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); - - struct wlr_box output_box = { - .width = output->swayc->width, - .height = output->swayc->height, + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, }; struct wlr_box intersection; @@ -158,8 +126,8 @@ static void output_view_for_each_surface(struct sway_view *view, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct render_data *data = user_data; - geo->x = view->swayc->current.view_x - data->output->swayc->x; - geo->y = view->swayc->current.view_y - data->output->swayc->y; + geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; + geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y; geo->width = view->swayc->current.view_width; geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO @@ -187,8 +155,8 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged, wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->swayc->x; - double oy = unmanaged_surface->ly - output->swayc->y; + double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; + double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; surface_for_each_surface(xsurface->surface, ox, oy, geo, iterator, user_data); @@ -274,26 +242,14 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, pixman_region32_t *output_damage = data->damage; float alpha = data->alpha; - struct wlr_texture *texture = NULL; - struct wlr_box box; - bool intersects; - - // If this is the main surface of a view, render the saved_buffer instead - // if it exists. It exists when we are mid-transaction. - if (data->view && data->view->saved_buffer && - data->view->surface == surface) { - texture = data->view->saved_buffer->texture; - intersects = get_view_box(&data->root_geo, data->output, data->view, - sx, sy, &box); - } else { - texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - intersects = get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (!texture) { + return; } + struct wlr_box box; + bool intersects = get_surface_box(&data->root_geo, data->output, surface, + sx, sy, &box); if (!intersects) { return; } @@ -394,58 +350,98 @@ static void render_view_surfaces(struct sway_view *view, view, &data.root_geo, 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; + + struct wlr_texture *texture = transaction_get_texture(view); + if (!texture) { + return; + } + struct wlr_box box = { + .x = view->swayc->current.view_x - output->swayc->current.swayc_x, + .y = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = view->swayc->current.view_width, + .height = view->swayc->current.view_height, + }; + + struct wlr_box output_box = { + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, + }; + + struct wlr_box intersection; + bool intersects = wlr_box_intersection(&output_box, &box, &intersection); + if (!intersects) { + return; + } + + scale_box(&box, wlr_output->scale); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + wlr_output->transform_matrix); + + render_texture(wlr_output, damage, texture, &box, matrix, alpha); +} + /** * 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->sway_view; - render_view_surfaces(view, output, damage, view->swayc->alpha); + if (view->swayc->instructions->length) { + render_saved_view(view, output, damage, view->swayc->alpha); + } else { + render_view_surfaces(view, output, damage, view->swayc->alpha); + } struct wlr_box box; float output_scale = output->wlr_output->scale; float color[4]; + struct sway_container_state *state = &con->current; - if (con->current.border != B_NONE) { - if (con->current.border_left) { + if (state->border != B_NONE) { + if (state->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->current.swayc_x; - box.y = con->current.view_y; - box.width = con->current.border_thickness; - box.height = con->current.view_height; + box.x = state->swayc_x; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (con->current.border_right) { - if (con->parent->children->length == 1 - && con->parent->current.layout == L_HORIZ) { + if (state->border_right) { + if (state->parent->current.children->length == 1 + && state->parent->current.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 = con->current.view_x + con->current.view_width; - box.y = con->current.view_y; - box.width = con->current.border_thickness; - box.height = con->current.view_height; + box.x = state->view_x + state->view_width; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (con->current.border_bottom) { - if (con->parent->children->length == 1 - && con->parent->current.layout == L_VERT) { + if (state->border_bottom) { + if (state->parent->current.children->length == 1 + && con->current.parent->current.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 = con->current.swayc_x; - box.y = con->current.view_y + con->current.view_height; - box.width = con->current.swayc_width; - box.height = con->current.border_thickness; + box.x = state->swayc_x; + box.y = state->view_y + state->view_height; + box.width = state->swayc_width; + box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -469,10 +465,13 @@ static void render_titlebar(struct sway_output *output, struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; + struct sway_container_state *state = &con->current; float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = con->parent->current.layout; - bool is_last_child = - con->parent->children->items[con->parent->children->length - 1] == con; + enum sway_container_layout layout = state->parent->current.layout; + list_t *children = state->parent->current.children; + bool is_last_child = children->items[children->length - 1] == con; + double output_x = output->swayc->current.swayc_x; + double output_y = output->swayc->current.swayc_y; // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); @@ -490,10 +489,8 @@ static void render_titlebar(struct sway_output *output, if (layout == L_HORIZ || layout == L_VERT || (layout == L_STACKED && is_last_child)) { if (con->type == C_VIEW) { - left_offset = - con->current.border_left * con->current.border_thickness; - right_offset = - con->current.border_right * con->current.border_thickness; + left_offset = state->border_left * state->border_thickness; + right_offset = state->border_right * state->border_thickness; connects_sides = true; } } @@ -527,10 +524,9 @@ static void render_titlebar(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(marks_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x - output->swayc->x + width - TITLEBAR_H_PADDING) + texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) * output_scale - texture_box.width; - texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) - * output_scale; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -551,10 +547,8 @@ static void render_titlebar(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(title_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x - output->swayc->x + TITLEBAR_H_PADDING) - * output_scale; - texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) - * output_scale; + texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -614,16 +608,15 @@ static void render_titlebar(struct sway_output *output, // Left pixel in line with bottom bar box.x = x; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = con->current.border_thickness * con->current.border_left; + box.width = state->border_thickness * state->border_left; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Right pixel in line with bottom bar - box.x = x + width - - con->current.border_thickness * con->current.border_right; + box.x = x + width - state->border_thickness * state->border_right; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = con->current.border_thickness * con->current.border_right; + box.width = state->border_thickness * state->border_right; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -636,7 +629,8 @@ static void render_titlebar(struct sway_output *output, static void render_top_border(struct sway_output *output, pixman_region32_t *output_damage, struct sway_container *con, struct border_colors *colors) { - if (!con->current.border_top) { + struct sway_container_state *state = &con->current; + if (!state->border_top) { return; } struct wlr_box box; @@ -646,10 +640,10 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->current.swayc_x; - box.y = con->current.swayc_y; - box.width = con->current.swayc_width; - box.height = con->current.border_thickness; + box.x = state->swayc_x; + box.y = state->swayc_y; + box.width = state->swayc_width; + box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } @@ -669,31 +663,34 @@ static void render_container_simple(struct sway_output *output, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; if (child->type == C_VIEW) { + struct sway_view *view = child->sway_view; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; + struct sway_container_state *state = &child->current; + if (focus == child || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; - marks_texture = child->sway_view->marks_focused; + marks_texture = view->marks_focused; } else if (seat_get_focus_inactive(seat, con) == child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; - marks_texture = child->sway_view->marks_focused_inactive; + marks_texture = view->marks_focused_inactive; } else { colors = &config->border_colors.unfocused; title_texture = child->title_unfocused; - marks_texture = child->sway_view->marks_unfocused; + marks_texture = view->marks_unfocused; } - if (child->current.border == B_NORMAL) { - render_titlebar(output, damage, child, child->current.swayc_x, - child->current.swayc_y, child->current.swayc_width, - colors, title_texture, marks_texture); + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->swayc_x, + state->swayc_y, state->swayc_width, colors, + title_texture, marks_texture); } else { render_top_border(output, damage, child, colors); } @@ -711,22 +708,23 @@ static void render_container_simple(struct sway_output *output, static void render_container_tabbed(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - if (!con->children->length) { + if (!con->current.children->length) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_child(seat, con); struct border_colors *current_colors = NULL; + struct sway_container_state *pstate = &con->current; // Render tabs - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - struct sway_view *view = - child->type == C_VIEW ? child->sway_view : NULL; if (focus == child || parent_focused) { colors = &config->border_colors.focused; @@ -735,22 +733,22 @@ static void render_container_tabbed(struct sway_output *output, } else if (child == current) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; - marks_texture = view ? view->marks_focused : NULL; + marks_texture = view ? view->marks_focused_inactive : NULL; } else { colors = &config->border_colors.unfocused; title_texture = child->title_unfocused; marks_texture = view ? view->marks_unfocused : NULL; } - int tab_width = con->current.swayc_width / con->children->length; - int x = con->current.swayc_x + tab_width * i; + int tab_width = pstate->swayc_width / pstate->children->length; + int x = pstate->swayc_x + tab_width * i; // Make last tab use the remaining width of the parent - if (i == con->children->length - 1) { - tab_width = con->current.swayc_width - tab_width * i; + if (i == pstate->children->length - 1) { + tab_width = pstate->swayc_width - tab_width * i; } - render_titlebar(output, damage, child, x, child->current.swayc_y, - tab_width, colors, title_texture, marks_texture); + render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, + colors, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -772,22 +770,23 @@ static void render_container_tabbed(struct sway_output *output, static void render_container_stacked(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - if (!con->children->length) { + if (!con->current.children->length) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_child(seat, con); struct border_colors *current_colors = NULL; + struct sway_container_state *pstate = &con->current; // Render titles - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - struct sway_view *view = - child->type == C_VIEW ? child->sway_view : NULL; if (focus == child || parent_focused) { colors = &config->border_colors.focused; @@ -803,10 +802,9 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = con->current.swayc_y + container_titlebar_height() * i; - render_titlebar(output, damage, child, child->current.swayc_x, y, - child->current.swayc_width, colors, - title_texture, marks_texture); + int y = pstate->swayc_y + container_titlebar_height() * i; + render_titlebar(output, damage, child, cstate->swayc_x, y, + cstate->swayc_width, colors, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -877,17 +875,18 @@ static void render_floating_container(struct sway_output *soutput, static void render_floating(struct sway_output *soutput, pixman_region32_t *damage) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - for (int j = 0; j < output->children->length; ++j) { - struct sway_container *workspace = output->children->items[j]; - struct sway_workspace *ws = workspace->sway_workspace; - if (!workspace_is_visible(workspace)) { + for (int i = 0; i < root_container.current.children->length; ++i) { + struct sway_container *output = + root_container.current.children->items[i]; + for (int j = 0; j < output->current.children->length; ++j) { + struct sway_container *ws = output->current.children->items[j]; + if (!workspace_is_visible(ws)) { continue; } - for (int k = 0; k < ws->floating->children->length; ++k) { - struct sway_container *floater = - ws->floating->children->items[k]; + list_t *floating = + ws->current.ws_floating->current.children; + for (int k = 0; k < floating->length; ++k) { + struct sway_container *floater = floating->items[k]; render_floating_container(soutput, damage, floater); } } @@ -901,7 +900,7 @@ static struct sway_container *output_get_active_workspace( seat_get_focus_inactive(seat, output->swayc); if (!focus) { // We've never been to this output before - focus = output->swayc->children->items[0]; + focus = output->swayc->current.children->items[0]; } struct sway_container *workspace = focus; if (workspace->type != C_WORKSPACE) { @@ -942,8 +941,9 @@ static void render_output(struct sway_output *output, struct timespec *when, } struct sway_container *workspace = output_get_active_workspace(output); + struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; - if (workspace->sway_workspace->fullscreen) { + if (fullscreen_view) { float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; int nrects; @@ -954,10 +954,9 @@ static void render_output(struct sway_output *output, struct timespec *when, } // TODO: handle views smaller than the output - render_view_surfaces( - workspace->sway_workspace->fullscreen, output, damage, 1.0f); + render_view_surfaces(fullscreen_view, output, damage, 1.0f); - if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { + if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); } @@ -1073,11 +1072,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { }; struct sway_container *workspace = output_get_active_workspace(output); - if (workspace->sway_workspace->fullscreen) { + if (workspace->current.ws_fullscreen) { send_frame_done_container_iterator( - workspace->sway_workspace->fullscreen->swayc, &data); + workspace->current.ws_fullscreen->swayc, &data); - if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { + if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 77377a18..6e09537a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -9,6 +9,7 @@ #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "list.h" #include "log.h" @@ -18,6 +19,13 @@ */ #define TIMEOUT_MS 200 +/** + * If enabled, sway will always wait for the transaction timeout before + * applying it, rather than applying it when the views are ready. This allows us + * to observe the rendered state while a transaction is in progress. + */ +#define TRANSACTION_DEBUG false + struct sway_transaction { struct wl_event_source *timer; list_t *instructions; // struct sway_transaction_instruction * @@ -29,7 +37,9 @@ struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_container *container; struct sway_container_state state; + struct wlr_buffer *saved_buffer; uint32_t serial; + bool ready; }; struct sway_transaction *transaction_create() { @@ -40,44 +50,55 @@ struct sway_transaction *transaction_create() { return transaction; } +static void remove_saved_view_buffer( + struct sway_transaction_instruction *instruction) { + if (instruction->saved_buffer) { + wlr_buffer_unref(instruction->saved_buffer); + instruction->saved_buffer = NULL; + } +} + +static void save_view_buffer(struct sway_view *view, + struct sway_transaction_instruction *instruction) { + if (!sway_assert(instruction->saved_buffer == NULL, + "Didn't expect instruction to have a saved buffer already")) { + remove_saved_view_buffer(instruction); + } + if (view->surface && wlr_surface_has_buffer(view->surface)) { + wlr_buffer_ref(view->surface->buffer); + instruction->saved_buffer = view->surface->buffer; + } +} + static void transaction_destroy(struct sway_transaction *transaction) { - int i; // Free instructions - for (i = 0; i < transaction->instructions->length; ++i) { + for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - if (instruction->container->type == C_VIEW) { - struct sway_view *view = instruction->container->sway_view; - for (int j = 0; j < view->instructions->length; ++j) { - if (view->instructions->items[j] == instruction) { - list_del(view->instructions, j); - break; - } + struct sway_container *con = instruction->container; + for (int j = 0; j < con->instructions->length; ++j) { + if (con->instructions->items[j] == instruction) { + list_del(con->instructions, j); + break; } } + if (con->destroying && !con->instructions->length) { + container_free(con); + } + remove_saved_view_buffer(instruction); free(instruction); } list_free(transaction->instructions); // Free damage - for (i = 0; i < transaction->damage->length; ++i) { - struct wlr_box *box = transaction->damage->items[i]; - free(box); - } + list_foreach(transaction->damage, free); list_free(transaction->damage); free(transaction); } -void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container) { - struct sway_transaction_instruction *instruction = - calloc(1, sizeof(struct sway_transaction_instruction)); - instruction->transaction = transaction; - instruction->container = container; - - // Copy the container's main (pending) properties into the instruction state - struct sway_container_state *state = &instruction->state; +static void copy_pending_state(struct sway_container *container, + struct sway_container_state *state) { state->layout = container->layout; state->swayc_x = container->x; state->swayc_y = container->y; @@ -87,6 +108,7 @@ void transaction_add_container(struct sway_transaction *transaction, state->current_gaps = container->current_gaps; state->gaps_inner = container->gaps_inner; state->gaps_outer = container->gaps_outer; + state->parent = container->parent; if (container->type == C_VIEW) { struct sway_view *view = container->sway_view; @@ -101,8 +123,44 @@ void transaction_add_container(struct sway_transaction *transaction, state->border_left = view->border_left; state->border_right = view->border_right; state->border_bottom = view->border_bottom; + } else if (container->type == C_WORKSPACE) { + state->ws_fullscreen = container->sway_workspace->fullscreen; + state->ws_floating = container->sway_workspace->floating; + state->children = create_list(); + list_cat(state->children, container->children); + } else { + state->children = create_list(); + list_cat(state->children, container->children); + } +} + +static bool transaction_has_container(struct sway_transaction *transaction, + struct sway_container *container) { + for (int i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + if (instruction->container == container) { + return true; + } + } + return false; +} + +void transaction_add_container(struct sway_transaction *transaction, + struct sway_container *container) { + if (transaction_has_container(transaction, container)) { + return; } + struct sway_transaction_instruction *instruction = + calloc(1, sizeof(struct sway_transaction_instruction)); + instruction->transaction = transaction; + instruction->container = container; + + copy_pending_state(container, &instruction->state); + if (container->type == C_VIEW) { + save_view_buffer(container->sway_view, instruction); + } list_add(transaction->instructions, instruction); } @@ -113,47 +171,29 @@ void transaction_add_damage(struct sway_transaction *transaction, list_add(transaction->damage, box); } -static void save_view_buffer(struct sway_view *view) { - if (view->saved_buffer) { - wlr_buffer_unref(view->saved_buffer); - } - wlr_buffer_ref(view->surface->buffer); - view->saved_buffer = view->surface->buffer; - view->saved_surface_width = view->surface->current->width; - view->saved_surface_height = view->surface->current->height; -} - -static void remove_saved_view_buffer(struct sway_view *view) { - if (view->saved_buffer) { - wlr_buffer_unref(view->saved_buffer); - view->saved_buffer = NULL; - view->saved_surface_width = 0; - view->saved_surface_height = 0; - } -} - /** * Apply a transaction to the "current" state of the tree. - * - * This is mostly copying stuff from the pending state into the main swayc - * properties, but also includes reparenting and deleting containers. */ static void transaction_apply(struct sway_transaction *transaction) { int i; + // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *container = instruction->container; - memcpy(&instruction->container->current, &instruction->state, - sizeof(struct sway_container_state)); + // 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. + // Any child containers which are being deleted will be cleaned up in + // transaction_destroy(). + list_free(container->current.children); - if (container->type == C_VIEW) { - remove_saved_view_buffer(container->sway_view); - } + memcpy(&container->current, &instruction->state, + sizeof(struct sway_container_state)); } - // Damage + // Apply damage for (i = 0; i < transaction->damage->length; ++i) { struct wlr_box *box = transaction->damage->items[i]; for (int j = 0; j < root_container.children->length; ++j) { @@ -161,8 +201,6 @@ static void transaction_apply(struct sway_transaction *transaction) { output_damage_box(output->sway_output, box); } } - - update_debug_tree(); } static int handle_timeout(void *data) { @@ -182,7 +220,7 @@ void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *con = instruction->container; - if (con->type == C_VIEW && + if (con->type == C_VIEW && !con->destroying && (con->current.view_width != instruction->state.view_width || con->current.view_height != instruction->state.view_height)) { instruction->serial = view_configure(con->sway_view, @@ -191,14 +229,12 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_buffer(con->sway_view); - list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } } + list_add(con->instructions, instruction); } if (!transaction->num_waiting) { - // This can happen if the transaction only contains xwayland views wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", transaction); transaction_apply(transaction); @@ -210,31 +246,47 @@ void transaction_commit(struct sway_transaction *transaction) { transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, handle_timeout, transaction); wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + + // The debug tree shows the pending/live tree. Here is a good place to + // update it, because we make a transaction every time we change the pending + // tree. + update_debug_tree(); } void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { // Find the instruction struct sway_transaction_instruction *instruction = NULL; - for (int i = 0; i < view->instructions->length; ++i) { + for (int i = 0; i < view->swayc->instructions->length; ++i) { struct sway_transaction_instruction *tmp_instruction = - view->instructions->items[i]; - if (tmp_instruction->serial == serial) { + view->swayc->instructions->items[i]; + if (tmp_instruction->serial == serial && !tmp_instruction->ready) { instruction = tmp_instruction; - list_del(view->instructions, i); break; } } if (!instruction) { - // This can happen if the view acknowledges the configure after the - // transaction has timed out and applied. return; } + instruction->ready = true; + // If all views are ready, apply the transaction struct sway_transaction *transaction = instruction->transaction; if (--transaction->num_waiting == 0) { +#if !TRANSACTION_DEBUG wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); wl_event_source_timer_update(transaction->timer, 0); transaction_apply(transaction); transaction_destroy(transaction); +#endif } } + +struct wlr_texture *transaction_get_texture(struct sway_view *view) { + if (!view->swayc || !view->swayc->instructions->length) { + return view->surface->buffer->texture; + } + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[0]; + return instruction->saved_buffer ? + instruction->saved_buffer->texture : NULL; +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d22c967c..ab35b98f 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -143,16 +143,12 @@ static void _close(struct sway_view *view) { } } -static void destroy(struct sway_view *view) { +static void _free(struct sway_view *view) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { return; } - wl_list_remove(&xdg_shell_view->destroy.link); - wl_list_remove(&xdg_shell_view->map.link); - wl_list_remove(&xdg_shell_view->unmap.link); - wl_list_remove(&xdg_shell_view->request_fullscreen.link); free(xdg_shell_view); } @@ -164,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .destroy = destroy, + .free = _free, }; static void handle_commit(struct wl_listener *listener, void *data) { @@ -173,7 +169,11 @@ 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_surface; - if (view->instructions->length) { + if (!view->swayc) { + return; + } + + if (view->swayc->instructions->length) { transaction_notify_view_ready(view, xdg_surface->configure_serial); } @@ -191,11 +191,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap); + struct sway_view *view = &xdg_shell_view->view; + + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } - view_unmap(&xdg_shell_view->view); + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); + view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -230,7 +237,17 @@ static void handle_map(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy); - view_destroy(&xdg_shell_view->view); + struct sway_view *view = &xdg_shell_view->view; + if (!sway_assert(view->swayc == NULL || view->swayc->destroying, + "Tried to destroy a mapped view")) { + return; + } + wl_list_remove(&xdg_shell_view->destroy.link); + wl_list_remove(&xdg_shell_view->map.link); + wl_list_remove(&xdg_shell_view->unmap.link); + wl_list_remove(&xdg_shell_view->request_fullscreen.link); + view->wlr_xdg_surface = NULL; + view_destroy(view); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 7ec9e6cb..76c1fa24 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -143,16 +143,12 @@ static void _close(struct sway_view *view) { } } -static void destroy(struct sway_view *view) { +static void _free(struct sway_view *view) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { return; } - wl_list_remove(&xdg_shell_v6_view->destroy.link); - wl_list_remove(&xdg_shell_v6_view->map.link); - wl_list_remove(&xdg_shell_v6_view->unmap.link); - wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); free(xdg_shell_v6_view); } @@ -164,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .destroy = destroy, + .free = _free, }; static void handle_commit(struct wl_listener *listener, void *data) { @@ -173,7 +169,10 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; - if (view->instructions->length) { + if (!view->swayc) { + return; + } + if (view->swayc->instructions->length) { transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } @@ -191,11 +190,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, unmap); + struct sway_view *view = &xdg_shell_v6_view->view; - view_unmap(&xdg_shell_v6_view->view); + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } + + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); + view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -230,7 +236,13 @@ static void handle_map(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, destroy); - view_destroy(&xdg_shell_v6_view->view); + struct sway_view *view = &xdg_shell_v6_view->view; + wl_list_remove(&xdg_shell_v6_view->destroy.link); + wl_list_remove(&xdg_shell_v6_view->map.link); + wl_list_remove(&xdg_shell_v6_view->unmap.link); + wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); + view->wlr_xdg_surface_v6 = NULL; + view_destroy(view); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 55917bf6..a1837420 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -218,19 +218,11 @@ static void _close(struct sway_view *view) { wlr_xwayland_surface_close(view->wlr_xwayland_surface); } -static void destroy(struct sway_view *view) { +static void _free(struct sway_view *view) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return; } - wl_list_remove(&xwayland_view->destroy.link); - wl_list_remove(&xwayland_view->request_configure.link); - wl_list_remove(&xwayland_view->request_fullscreen.link); - wl_list_remove(&xwayland_view->set_title.link); - wl_list_remove(&xwayland_view->set_class.link); - wl_list_remove(&xwayland_view->set_window_type.link); - wl_list_remove(&xwayland_view->map.link); - wl_list_remove(&xwayland_view->unmap.link); free(xwayland_view); } @@ -242,7 +234,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .close = _close, - .destroy = destroy, + .free = _free, }; static void handle_commit(struct wl_listener *listener, void *data) { @@ -254,7 +246,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { // Don't allow xwayland views to do resize or reposition themselves if // they're involved in a transaction. Once the transaction has finished // they'll apply the next time a commit happens. - if (view->instructions->length) { + if (view->swayc && view->swayc->instructions->length) { if (view->swayc && container_is_floating(view->swayc)) { view_update_size(view, xsurface->width, xsurface->height); } else { @@ -268,8 +260,17 @@ static void handle_commit(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap); + struct sway_view *view = &xwayland_view->view; + + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } + + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); + wl_list_remove(&xwayland_view->commit.link); - view_unmap(&xwayland_view->view); + view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -293,12 +294,30 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); } - arrange_and_commit(view->swayc); + arrange_and_commit(view->swayc->parent); } static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy); + struct sway_view *view = &xwayland_view->view; + + if (view->surface) { + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); + + wl_list_remove(&xwayland_view->commit.link); + view->surface = NULL; + } + + wl_list_remove(&xwayland_view->destroy.link); + wl_list_remove(&xwayland_view->request_configure.link); + wl_list_remove(&xwayland_view->request_fullscreen.link); + wl_list_remove(&xwayland_view->set_title.link); + wl_list_remove(&xwayland_view->set_class.link); + wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); view_destroy(&xwayland_view->view); } diff --git a/sway/main.c b/sway/main.c index a7e808ad..a83660d5 100644 --- a/sway/main.c +++ b/sway/main.c @@ -34,6 +34,7 @@ struct sway_server server; void sway_terminate(int exit_code) { terminate_request = true; exit_value = exit_code; + server.terminating = true; wl_display_terminate(server.wl_display); } diff --git a/sway/server.c b/sway/server.c index 824b1d8e..a13f2c3a 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,6 +19,7 @@ #include // TODO WLR: make Xwayland optional #include +#include "list.h" #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/server.h" @@ -105,6 +106,8 @@ bool server_init(struct sway_server *server) { return false; } + server->destroying_containers = create_list(); + input_manager = input_manager_create(server); return true; } @@ -112,6 +115,7 @@ bool server_init(struct sway_server *server) { void server_fini(struct sway_server *server) { // TODO: free sway-specific resources wl_display_destroy(server->wl_display); + list_free(server->destroying_containers); } void server_run(struct sway_server *server) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index ac99c5df..cb3f8ba2 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -144,6 +144,19 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { } } +/** + * If a container has been deleted from the pending tree state, we must add it + * to the transaction so it can be freed afterwards. To do this, we iterate the + * server's destroying_containers list and add all of them. We may add more than + * what we need to, but this is easy and has no negative consequences. + */ +static void add_deleted_containers(struct sway_transaction *transaction) { + for (int i = 0; i < server.destroying_containers->length; ++i) { + struct sway_container *child = server.destroying_containers->items[i]; + transaction_add_container(transaction, child); + } +} + static void arrange_children_of(struct sway_container *parent, struct sway_transaction *transaction); @@ -158,6 +171,7 @@ static void arrange_floating(struct sway_container *floating, } transaction_add_container(transaction, floater); } + transaction_add_container(transaction, floating); } static void arrange_children_of(struct sway_container *parent, @@ -290,7 +304,16 @@ void arrange_windows(struct sway_container *container, case C_TYPES: break; } - transaction_add_damage(transaction, container_get_box(container)); + // Add damage for whatever container arrange_windows() was called with, + // unless it was called with the special floating container, in which case + // we'll damage the entire output. + if (container->type == C_CONTAINER && container->layout == L_FLOATING) { + struct sway_container *output = container_parent(container, C_OUTPUT); + transaction_add_damage(transaction, container_get_box(output)); + } else { + transaction_add_damage(transaction, container_get_box(container)); + } + add_deleted_containers(transaction); } void arrange_and_commit(struct sway_container *container) { diff --git a/sway/tree/container.c b/sway/tree/container.c index b071f394..484d26a5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -16,7 +16,6 @@ #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -113,10 +112,11 @@ struct sway_container *container_create(enum sway_container_type type) { c->layout = L_NONE; c->type = type; c->alpha = 1.0f; + c->instructions = create_list(); if (type != C_VIEW) { c->children = create_list(); - //c->pending.children = create_list(); + c->current.children = create_list(); } wl_signal_init(&c->events.destroy); @@ -133,43 +133,68 @@ struct sway_container *container_create(enum sway_container_type type) { return c; } -static void _container_destroy(struct sway_container *cont) { - if (cont == NULL) { - return; - } - - wl_signal_emit(&cont->events.destroy, cont); +static void container_workspace_free(struct sway_workspace *ws) { + list_foreach(ws->output_priority, free); + list_free(ws->output_priority); + ws->floating->destroying = true; + container_free(ws->floating); + free(ws); +} - struct sway_container *parent = cont->parent; - if (cont->children != NULL && cont->children->length) { - // remove children until there are no more, container_destroy calls - // container_remove_child, which removes child from this container - while (cont->children != NULL && cont->children->length > 0) { - struct sway_container *child = cont->children->items[0]; - ipc_event_window(child, "close"); - container_remove_child(child); - _container_destroy(child); - } - } - if (cont->marks) { - list_foreach(cont->marks, free); - list_free(cont->marks); - } - if (parent) { - parent = container_remove_child(cont); +void container_free(struct sway_container *cont) { + if (!sway_assert(cont->destroying, + "Tried to free container which wasn't marked as destroying")) { + return; } - if (cont->name) { - free(cont->name); + if (!sway_assert(cont->instructions->length == 0, + "Tried to free container with pending instructions")) { + return; } - + free(cont->name); wlr_texture_destroy(cont->title_focused); wlr_texture_destroy(cont->title_focused_inactive); wlr_texture_destroy(cont->title_unfocused); wlr_texture_destroy(cont->title_urgent); + for (int i = 0; i < server.destroying_containers->length; ++i) { + if (server.destroying_containers->items[i] == cont) { + list_del(server.destroying_containers, i); + break; + } + } + + list_free(cont->instructions); list_free(cont->children); - //list_free(cont->pending.children); - cont->children = NULL; + list_free(cont->current.children); + + switch (cont->type) { + case C_ROOT: + break; + case C_OUTPUT: + cont->sway_output->swayc = NULL; + break; + case C_WORKSPACE: + container_workspace_free(cont->sway_workspace); + break; + case C_CONTAINER: + break; + case C_VIEW: + { + struct sway_view *view = cont->sway_view; + view->swayc = NULL; + free(view->title_format); + view->title_format = NULL; + + if (view->destroying) { + view_free(view); + } + } + break; + case C_TYPES: + sway_assert(false, "Didn't expect to see C_TYPES here"); + break; + } + free(cont); } @@ -186,7 +211,6 @@ static struct sway_container *container_workspace_destroy( } wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); - ipc_event_window(workspace, "close"); struct sway_container *parent = workspace->parent; if (!workspace_is_empty(workspace) && output) { @@ -209,25 +233,6 @@ static struct sway_container *container_workspace_destroy( container_move_to(floating->children->items[i], new_workspace->sway_workspace->floating); } - arrange_and_commit(new_workspace); - } - - struct sway_workspace *sway_workspace = workspace->sway_workspace; - - // This emits the destroy event and also destroys the swayc. - _container_destroy(workspace); - - // Clean up the floating container - sway_workspace->floating->parent = NULL; - _container_destroy(sway_workspace->floating); - - list_foreach(sway_workspace->output_priority, free); - list_free(sway_workspace->output_priority); - - free(sway_workspace); - - if (output) { - output_damage_whole(output->sway_output); } return parent; @@ -266,14 +271,13 @@ static struct sway_container *container_output_destroy( container_add_child(new_output, workspace); ipc_event_workspace(workspace, NULL, "move"); } else { - container_workspace_destroy(workspace); + container_destroy(workspace); } container_sort_workspaces(new_output); } } } - arrange_and_commit(&root_container); wl_list_remove(&output->sway_output->mode.link); wl_list_remove(&output->sway_output->transform.link); @@ -285,12 +289,8 @@ static struct sway_container *container_output_destroy( output->sway_output->swayc = NULL; wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); - _container_destroy(output); - return &root_container; -} -static void container_root_finish(struct sway_container *con) { - wlr_log(L_ERROR, "TODO: destroy the root container"); + return &root_container; } bool container_reap_empty(struct sway_container *con) { @@ -306,13 +306,13 @@ bool container_reap_empty(struct sway_container *con) { case C_WORKSPACE: if (!workspace_is_visible(con) && workspace_is_empty(con)) { wlr_log(L_DEBUG, "Destroying workspace via reaper"); - container_workspace_destroy(con); + container_destroy(con); return true; } break; case C_CONTAINER: if (con->children->length == 0) { - _container_destroy(con); + container_destroy(con); return true; } case C_VIEW: @@ -349,46 +349,48 @@ struct sway_container *container_flatten(struct sway_container *container) { return container; } +/** + * container_destroy() is the first step in destroying a container. We'll emit + * events, detach it from the tree and mark it as destroying. The container will + * remain in memory until it's no longer used by a transaction, then it will be + * freed via container_free(). + */ struct sway_container *container_destroy(struct sway_container *con) { if (con == NULL) { return NULL; } + if (con->destroying) { + return NULL; + } - struct sway_container *parent = con->parent; + // The below functions move their children to somewhere else. + if (con->type == C_OUTPUT) { + container_output_destroy(con); + } else if (con->type == C_WORKSPACE) { + // Workspaces will refuse to be destroyed if they're the last workspace + // on their output. + if (!container_workspace_destroy(con)) { + return NULL; + } + } - switch (con->type) { - case C_ROOT: - container_root_finish(con); - break; - case C_OUTPUT: - // dont try to reap the root after this - container_output_destroy(con); - break; - case C_WORKSPACE: - // dont try to reap the output after this - container_workspace_destroy(con); - break; - case C_CONTAINER: - if (con->children->length) { - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[0]; - ipc_event_window(child, "close"); - container_remove_child(child); - container_add_child(parent, child); - } - } - ipc_event_window(con, "close"); - _container_destroy(con); - break; - case C_VIEW: - _container_destroy(con); - break; - case C_TYPES: - wlr_log(L_ERROR, "container_destroy called on an invalid " - "container"); - break; + // At this point the container being destroyed shouldn't have any children + // unless sway is terminating. + if (!server.terminating) { + if (!sway_assert(!con->children || con->children->length == 0, + "Didn't expect to see children here")) { + return NULL; + } } + wl_signal_emit(&con->events.destroy, con); + ipc_event_window(con, "close"); + + struct sway_container *parent = container_remove_child(con); + + con->destroying = true; + list_add(server.destroying_containers, con); + return container_reap_empty_recursive(parent); } diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 3724361d..14631ad4 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -30,7 +30,9 @@ void layout_init(void) { root_container.type = C_ROOT; root_container.layout = L_NONE; root_container.name = strdup("root"); + root_container.instructions = create_list(); root_container.children = create_list(); + root_container.current.children = create_list(); wl_signal_init(&root_container.events.destroy); root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); diff --git a/sway/tree/output.c b/sway/tree/output.c index 8af319d5..e2927cdb 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -29,7 +29,6 @@ static void restore_workspaces(struct sway_container *output) { } container_sort_workspaces(output); - arrange_and_commit(&root_container); } struct sway_container *output_create( @@ -66,7 +65,6 @@ struct sway_container *output_create( struct sway_container *output = container_create(C_OUTPUT); output->sway_output = sway_output; - sway_output->swayc = output; output->name = strdup(name); if (output->name == NULL) { container_destroy(output); diff --git a/sway/tree/view.c b/sway/tree/view.c index 658a94e8..cb36f123 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,47 +25,60 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->impl = impl; view->executed_criteria = create_list(); view->marks = create_list(); - view->instructions = create_list(); wl_signal_init(&view->events.unmap); } -void view_destroy(struct sway_view *view) { - if (view == NULL) { +void view_free(struct sway_view *view) { + if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { return; } - - if (view->surface != NULL) { - view_unmap(view); + if (!sway_assert(view->destroying, + "Tried to free view which wasn't marked as destroying")) { + return; } - - if (!sway_assert(view->instructions->length == 0, - "Tried to destroy view with pending instructions")) { + if (!sway_assert(view->swayc == NULL, + "Tried to free view which still has a swayc " + "(might have a pending transaction?)")) { return; } - list_free(view->executed_criteria); - for (int i = 0; i < view->marks->length; ++i) { - free(view->marks->items[i]); - } + list_foreach(view->marks, free); list_free(view->marks); - list_free(view->instructions); - wlr_texture_destroy(view->marks_focused); wlr_texture_destroy(view->marks_focused_inactive); wlr_texture_destroy(view->marks_unfocused); wlr_texture_destroy(view->marks_urgent); - container_destroy(view->swayc); - - if (view->impl->destroy) { - view->impl->destroy(view); + if (view->impl->free) { + view->impl->free(view); } else { free(view); } } +/** + * The view may or may not be involved in a transaction. For example, a view may + * unmap then attempt to destroy itself before we've applied the new layout. If + * an unmapping view is still involved in a transaction then it'll still have a + * swayc. + * + * If there's no transaction we can simply free the view. Otherwise the + * destroying flag will make the view get freed when the transaction is + * finished. + */ +void view_destroy(struct sway_view *view) { + if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { + return; + } + view->destroying = true; + + if (!view->swayc) { + view_free(view); + } +} + const char *view_get_title(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_TITLE); @@ -356,6 +369,9 @@ static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) { 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 { @@ -523,11 +539,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_handle_container_reparent(&view->container_reparent, NULL); } -void view_unmap(struct sway_view *view) { - if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) { - return; - } - +struct sway_container *view_unmap(struct sway_view *view) { wl_signal_emit(&view->events.unmap, view); if (view->is_fullscreen) { @@ -535,22 +547,10 @@ void view_unmap(struct sway_view *view) { ws->sway_workspace->fullscreen = NULL; } - container_damage_whole(view->swayc); - wl_list_remove(&view->surface_new_subsurface.link); wl_list_remove(&view->container_reparent.link); - struct sway_container *parent = container_destroy(view->swayc); - - view->swayc = NULL; - view->surface = NULL; - - if (view->title_format) { - free(view->title_format); - view->title_format = NULL; - } - - arrange_and_commit(parent); + return container_destroy(view->swayc); } void view_update_position(struct sway_view *view, double lx, double ly) { @@ -924,7 +924,7 @@ void view_update_marks_textures(struct sway_view *view) { } bool view_is_visible(struct sway_view *view) { - if (!view->swayc) { + if (!view->swayc || view->swayc->destroying) { return false; } struct sway_container *workspace = diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index ead752ad..5eb4be0f 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -430,6 +430,9 @@ bool workspace_switch(struct sway_container *workspace) { } bool workspace_is_visible(struct sway_container *ws) { + if (ws->destroying) { + return false; + } struct sway_container *output = container_parent(ws, C_OUTPUT); struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus_inactive(seat, output); -- cgit v1.2.3-54-g00ecf From 32b865e610dd937af17ce36b8c986e41f55a4627 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 23 Jun 2018 17:47:28 +1000 Subject: Fix crash when deleting last child in a tabbed or stacked container There was no `current` child because the container was destroyed. This makes it fall back to looking in the parent's current children list. --- include/sway/input/seat.h | 11 +++++++++++ sway/desktop/output.c | 28 ++++++++++++++++------------ sway/input/seat.c | 12 ++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 1f7792ba..0e440701 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -118,6 +118,17 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *seat_get_active_child(struct sway_seat *seat, struct sway_container *container); +/** + * Return the immediate child of container which was most recently focused, with + * fallback to selecting the child in the parent's `current` (rendered) children + * list. + * + * This is useful for when a tabbed container and its children are destroyed but + * still being rendered, and we have to render an appropriate child. + */ +struct sway_container *seat_get_active_current_child(struct sway_seat *seat, + struct sway_container *container); + /** * Iterate over the focus-inactive children of the container calling the * function on each. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9db95ef5..1ca48975 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -713,7 +713,7 @@ static void render_container_tabbed(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_child(seat, con); + struct sway_container *current = seat_get_active_current_child(seat, con); struct border_colors *current_colors = NULL; struct sway_container_state *pstate = &con->current; @@ -756,11 +756,13 @@ static void render_container_tabbed(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } } @@ -775,7 +777,7 @@ static void render_container_stacked(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_child(seat, con); + struct sway_container *current = seat_get_active_current_child(seat, con); struct border_colors *current_colors = NULL; struct sway_container_state *pstate = &con->current; @@ -812,11 +814,13 @@ static void render_container_stacked(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 1ea36466..436d18e2 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -829,6 +829,18 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat, return NULL; } +struct sway_container *seat_get_active_current_child(struct sway_seat *seat, + struct sway_container *container) { + struct sway_container *child = seat_get_active_child(seat, container); + if (child) { + return child; + } + if (container->current.children->length == 1) { + return container->current.children->items[0]; + } + return NULL; +} + struct sway_container *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; -- cgit v1.2.3-54-g00ecf From f08a30d6d04b5f986ea1e66a017e81bcd7c77e39 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 12:33:23 +1000 Subject: Force transactions to complete in order This forces transactions to complete in order by using a singly linked list stored in the sway server. --- include/sway/server.h | 2 ++ sway/desktop/transaction.c | 90 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 74 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sway/server.h b/include/sway/server.h index b07e86a7..2aa7b7fe 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,6 +47,8 @@ struct sway_server { bool terminating; + struct sway_transaction *head_transaction; // singly linked list + // When a view is being destroyed and is waiting for a transaction to // complete it will be stored here. list_t *destroying_containers; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 6e09537a..04142bcc 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -31,6 +31,7 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * list_t *damage; // struct wlr_box * size_t num_waiting; + struct sway_transaction *next; }; struct sway_transaction_instruction { @@ -175,6 +176,7 @@ void transaction_add_damage(struct sway_transaction *transaction, * Apply a transaction to the "current" state of the tree. */ static void transaction_apply(struct sway_transaction *transaction) { + wlr_log(L_DEBUG, "Applying transaction %p", transaction); int i; // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { @@ -203,15 +205,54 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +static void progress_queue() { + struct sway_transaction *transaction = server.head_transaction; + struct sway_transaction *next = NULL; + while (transaction && !transaction->num_waiting) { + next = transaction->next; + transaction_apply(transaction); + transaction_destroy(transaction); + transaction = next; + } + server.head_transaction = transaction; +} + static int handle_timeout(void *data) { struct sway_transaction *transaction = data; - wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting), applying anyway", + wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", transaction, transaction->num_waiting); - transaction_apply(transaction); - transaction_destroy(transaction); + transaction->num_waiting = 0; + progress_queue(); return 0; } +static bool should_configure(struct sway_container *con, + struct sway_transaction_instruction *instruction) { + if (con->type != C_VIEW) { + return false; + } + if (con->destroying) { + return false; + } + // The settled dimensions are what size the view will be once any pending + // configures have applied (excluding the one we might be configuring now). + // If these match the dimensions that this transaction wants then we don't + // need to configure it. + int settled_width = con->current.view_width; + int settled_height = con->current.view_height; + if (con->instructions->length) { + struct sway_transaction_instruction *last_instruction = + con->instructions->items[con->instructions->length - 1]; + settled_width = last_instruction->state.view_width; + settled_height = last_instruction->state.view_height; + } + if (settled_width == instruction->state.view_width && + settled_height == instruction->state.view_height) { + return false; + } + return true; +} + void transaction_commit(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", transaction, transaction->instructions->length); @@ -220,9 +261,7 @@ void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *con = instruction->container; - if (con->type == C_VIEW && !con->destroying && - (con->current.view_width != instruction->state.view_width || - con->current.view_height != instruction->state.view_height)) { + if (should_configure(con, instruction)) { instruction->serial = view_configure(con->sway_view, instruction->state.view_x, instruction->state.view_y, @@ -234,18 +273,33 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } - if (!transaction->num_waiting) { - wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", - transaction); + if (server.head_transaction) { + // There is another transaction in progress - we must add this one to + // the queue so we complete after it. + struct sway_transaction *tail = server.head_transaction; + while (tail->next) { + tail = tail->next; + } + tail->next = transaction; + } else if (transaction->num_waiting) { + // There are no other transactions, but we're not applying immediately + // so we must jump in the queue so others will queue behind us. + server.head_transaction = transaction; + } else { + // There are no other transactions in progress, and this one has nothing + // to wait for, so we can skip the queue. + wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); transaction_apply(transaction); transaction_destroy(transaction); return; } - // Set up a timer which the views must respond within - transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, - handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + if (transaction->num_waiting) { + // Set up a timer which the views must respond within + transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_timeout, transaction); + wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + } // The debug tree shows the pending/live tree. Here is a good place to // update it, because we make a transaction every time we change the pending @@ -269,14 +323,14 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { } instruction->ready = true; - // If all views are ready, apply the transaction + // If all views are ready, apply the transaction. + // If the transaction has timed out then its num_waiting will be 0 already. struct sway_transaction *transaction = instruction->transaction; - if (--transaction->num_waiting == 0) { + if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { #if !TRANSACTION_DEBUG - wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); + wlr_log(L_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - transaction_apply(transaction); - transaction_destroy(transaction); + progress_queue(); #endif } } -- cgit v1.2.3-54-g00ecf From 1549fb719ae75a498bf319db45281464e72c759e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 23:01:09 +1000 Subject: Implement atomic layout updates for xwayland views --- include/sway/desktop/transaction.h | 9 ++++++++ sway/desktop/transaction.c | 44 +++++++++++++++++++++++--------------- sway/desktop/xwayland.c | 14 ++++-------- 3 files changed, 40 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index d6adc609..b1da86f1 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -49,6 +49,15 @@ void transaction_commit(struct sway_transaction *transaction); */ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); +/** + * Notify the transaction system that a view is ready for the new layout, but + * identifying the instruction by width and height rather than by serial. + * + * This is used by xwayland views, as they don't have serials. + */ +void transaction_notify_view_ready_by_size(struct sway_view *view, + int width, int height); + /** * Get the texture that should be rendered for a view. * diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 04142bcc..08678b5b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -267,9 +267,7 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_y, instruction->state.view_width, instruction->state.view_height); - if (instruction->serial) { - ++transaction->num_waiting; - } + ++transaction->num_waiting; } list_add(con->instructions, instruction); } @@ -307,20 +305,8 @@ void transaction_commit(struct sway_transaction *transaction) { update_debug_tree(); } -void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { - // Find the instruction - struct sway_transaction_instruction *instruction = NULL; - for (int i = 0; i < view->swayc->instructions->length; ++i) { - struct sway_transaction_instruction *tmp_instruction = - view->swayc->instructions->items[i]; - if (tmp_instruction->serial == serial && !tmp_instruction->ready) { - instruction = tmp_instruction; - break; - } - } - if (!instruction) { - return; - } +static void set_instruction_ready( + struct sway_transaction_instruction *instruction) { instruction->ready = true; // If all views are ready, apply the transaction. @@ -335,6 +321,30 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { } } +void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { + for (int i = 0; i < view->swayc->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + if (instruction->serial == serial && !instruction->ready) { + set_instruction_ready(instruction); + return; + } + } +} + +void transaction_notify_view_ready_by_size(struct sway_view *view, + int width, int height) { + for (int i = 0; i < view->swayc->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + if (!instruction->ready && instruction->state.view_width == width && + instruction->state.view_height == height) { + set_instruction_ready(instruction); + return; + } + } +} + struct wlr_texture *transaction_get_texture(struct sway_view *view) { if (!view->swayc || !view->swayc->instructions->length) { return view->surface->buffer->texture; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index a1837420..7e78ef32 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -7,6 +7,7 @@ #include #include "log.h" #include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/output.h" @@ -243,16 +244,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - // Don't allow xwayland views to do resize or reposition themselves if - // they're involved in a transaction. Once the transaction has finished - // they'll apply the next time a commit happens. - if (view->swayc && view->swayc->instructions->length) { - if (view->swayc && container_is_floating(view->swayc)) { - view_update_size(view, xsurface->width, xsurface->height); - } else { - view_update_size(view, view->swayc->width, view->swayc->height); - } - view_update_position(view, view->x, view->y); + if (view->swayc->instructions->length) { + transaction_notify_view_ready_by_size(view, + xsurface->width, xsurface->height); } view_damage_from(view); } -- cgit v1.2.3-54-g00ecf From 289d696adc22a4247345cb544454bac0dfd5d828 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 09:09:43 +1000 Subject: Implement transaction timings debug Launch sway with SWAY_DEBUG=txn_timings to enable it. --- include/sway/server.h | 2 ++ sway/desktop/transaction.c | 23 +++++++++++++++++++++++ sway/server.c | 4 ++++ 3 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/sway/server.h b/include/sway/server.h index 2aa7b7fe..94e8f2a2 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -45,6 +45,8 @@ struct sway_server { struct wlr_wl_shell *wl_shell; struct wl_listener wl_shell_surface; + bool debug_txn_timings; + bool terminating; struct sway_transaction *head_transaction; // singly linked list diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 08678b5b..cb23ab69 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "sway/debug.h" @@ -32,6 +33,8 @@ struct sway_transaction { list_t *damage; // struct wlr_box * size_t num_waiting; struct sway_transaction *next; + struct timespec create_time; + struct timespec commit_time; }; struct sway_transaction_instruction { @@ -48,6 +51,9 @@ struct sway_transaction *transaction_create() { calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); transaction->damage = create_list(); + if (server.debug_txn_timings) { + clock_gettime(CLOCK_MONOTONIC, &transaction->create_time); + } return transaction; } @@ -177,6 +183,20 @@ void transaction_add_damage(struct sway_transaction *transaction, */ static void transaction_apply(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Applying transaction %p", transaction); + if (server.debug_txn_timings) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec *create = &transaction->create_time; + struct timespec *commit = &transaction->commit_time; + float ms_arranging = (commit->tv_sec - create->tv_sec) * 1000 + + (commit->tv_nsec - create->tv_nsec) / 1000000.0; + float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + + (now.tv_nsec - commit->tv_nsec) / 1000000.0; + float ms_total = ms_arranging + ms_waiting; + wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " + "%.1fms total (%.1f frames if 60hz)", transaction, + ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); + } int i; // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { @@ -271,6 +291,9 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } + if (server.debug_txn_timings) { + clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); + } if (server.head_transaction) { // There is another transaction in progress - we must add this one to // the queue so we complete after it. diff --git a/sway/server.c b/sway/server.c index 86d4a643..a2bc5702 100644 --- a/sway/server.c +++ b/sway/server.c @@ -113,6 +113,10 @@ bool server_init(struct sway_server *server) { return false; } + const char *debug = getenv("SWAY_DEBUG"); + if (debug != NULL && strcmp(debug, "txn_timings") == 0) { + server->debug_txn_timings = true; + } server->destroying_containers = create_list(); input_manager = input_manager_create(server); -- cgit v1.2.3-54-g00ecf From 7a922c65aab27c5f4282cf15de52d240e5ac8052 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 13:15:45 +1000 Subject: Damage output when a fullscreen view unmaps Also moved the arranging into view_unmap to avoid excessive code duplication. --- include/sway/tree/view.h | 5 +---- sway/desktop/xdg_shell.c | 3 +-- sway/desktop/xdg_shell_v6.c | 3 +-- sway/desktop/xwayland.c | 7 ++----- sway/tree/view.c | 14 ++++++++++---- 5 files changed, 15 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5a615b43..0e6f5292 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -245,10 +245,7 @@ void view_destroy(struct sway_view *view); void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); -/** - * Unmap the view and return the surviving parent (after reaping). - */ -struct sway_container *view_unmap(struct sway_view *view); +void view_unmap(struct sway_view *view); void view_update_position(struct sway_view *view, double lx, double ly); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ab35b98f..a06c3bd2 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -197,8 +197,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 76c1fa24..424bca7b 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -196,8 +196,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7e78ef32..53fa42cc 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -260,8 +260,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xwayland_view->commit.link); view->surface = NULL; @@ -297,9 +296,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_view *view = &xwayland_view->view; if (view->surface) { - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); - + view_unmap(view); wl_list_remove(&xwayland_view->commit.link); view->surface = NULL; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 2ca0dbbb..5a78112a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -539,7 +539,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_handle_container_reparent(&view->container_reparent, NULL); } -struct sway_container *view_unmap(struct sway_view *view) { +void view_unmap(struct sway_view *view) { wl_signal_emit(&view->events.unmap, view); wl_list_remove(&view->surface_new_subsurface.link); @@ -549,10 +549,16 @@ struct sway_container *view_unmap(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); ws->sway_workspace->fullscreen = NULL; container_destroy(view->swayc); - return ws; - } - return container_destroy(view->swayc); + struct sway_container *output = ws->parent; + struct sway_transaction *transaction = transaction_create(); + arrange_windows(output, transaction); + transaction_add_damage(transaction, container_get_box(output)); + transaction_commit(transaction); + } else { + struct sway_container *parent = container_destroy(view->swayc); + arrange_and_commit(parent); + } } void view_update_position(struct sway_view *view, double lx, double ly) { -- cgit v1.2.3-54-g00ecf From 50190bc7609d981c45d26cd0b7d6d0fbf66feb05 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 13:18:33 +1000 Subject: Rename view's free callback to destroy --- include/sway/tree/view.h | 2 +- sway/desktop/xdg_shell.c | 4 ++-- sway/desktop/xdg_shell_v6.c | 4 ++-- sway/desktop/xwayland.c | 4 ++-- sway/tree/view.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0e6f5292..1bcb0582 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -37,7 +37,7 @@ struct sway_view_impl { void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); - void (*free)(struct sway_view *view); + void (*destroy)(struct sway_view *view); }; struct sway_view { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index a06c3bd2..105e77ae 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -143,7 +143,7 @@ static void _close(struct sway_view *view) { } } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { @@ -160,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 424bca7b..19b30604 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -143,7 +143,7 @@ static void _close(struct sway_view *view) { } } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { @@ -160,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 53fa42cc..eea8420d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -219,7 +219,7 @@ static void _close(struct sway_view *view) { wlr_xwayland_surface_close(view->wlr_xwayland_surface); } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return; @@ -235,7 +235,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 5a78112a..a616af03 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -51,8 +51,8 @@ void view_free(struct sway_view *view) { wlr_texture_destroy(view->marks_unfocused); wlr_texture_destroy(view->marks_urgent); - if (view->impl->free) { - view->impl->free(view); + if (view->impl->destroy) { + view->impl->destroy(view); } else { free(view); } -- cgit v1.2.3-54-g00ecf From a7b3f29292cad029f010aa8b5fafb56b08ba4ed7 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 20:18:57 +1000 Subject: Remove incorrect assertion and supporting code Children can exist when destroying a container, such as when destroying the last output. Sway is not terminating in that case. --- include/sway/server.h | 2 -- sway/main.c | 1 - sway/tree/container.c | 9 --------- 3 files changed, 12 deletions(-) (limited to 'include') diff --git a/include/sway/server.h b/include/sway/server.h index 94e8f2a2..bd96b085 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,8 +47,6 @@ struct sway_server { bool debug_txn_timings; - bool terminating; - struct sway_transaction *head_transaction; // singly linked list // When a view is being destroyed and is waiting for a transaction to diff --git a/sway/main.c b/sway/main.c index 61ae6a5f..a325dc3a 100644 --- a/sway/main.c +++ b/sway/main.c @@ -34,7 +34,6 @@ struct sway_server server; void sway_terminate(int exit_code) { terminate_request = true; exit_value = exit_code; - server.terminating = true; wl_display_terminate(server.wl_display); } diff --git a/sway/tree/container.c b/sway/tree/container.c index 0c860405..2b9eb636 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -319,15 +319,6 @@ struct sway_container *container_destroy_noreaping(struct sway_container *con) { } } - // At this point the container being destroyed shouldn't have any children - // unless sway is terminating. - if (!server.terminating) { - if (!sway_assert(!con->children || con->children->length == 0, - "Didn't expect to see children here")) { - return NULL; - } - } - con->destroying = true; list_add(server.destroying_containers, con); -- cgit v1.2.3-54-g00ecf From be86d3aba602fef7b51fafa8a6e7a39d1e49817f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:46:03 +1000 Subject: Remove transaction_add_damage Instead, damage each container when applying the transaction. --- include/sway/desktop/transaction.h | 7 ------- sway/desktop/transaction.c | 42 ++++++++++++++++++-------------------- sway/desktop/xdg_shell.c | 5 +---- sway/desktop/xdg_shell_v6.c | 5 +---- sway/desktop/xwayland.c | 5 +---- sway/tree/arrange.c | 9 -------- sway/tree/view.c | 6 +----- 7 files changed, 24 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index b1da86f1..fcfed297 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -30,13 +30,6 @@ struct sway_transaction *transaction_create(void); void transaction_add_container(struct sway_transaction *transaction, struct sway_container *container); -/** - * Add a box to be damaged when the transaction is applied. - * The box should be in layout coordinates. - */ -void transaction_add_damage(struct sway_transaction *transaction, - struct wlr_box *box); - /** * Submit a transaction to the client views for configuration. */ diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 98cde889..c29b6661 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -30,7 +30,6 @@ struct sway_transaction { struct wl_event_source *timer; list_t *instructions; // struct sway_transaction_instruction * - list_t *damage; // struct wlr_box * size_t num_waiting; size_t num_configures; struct sway_transaction *next; @@ -51,7 +50,6 @@ struct sway_transaction *transaction_create() { struct sway_transaction *transaction = calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); - transaction->damage = create_list(); if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->create_time); } @@ -97,10 +95,6 @@ static void transaction_destroy(struct sway_transaction *transaction) { } list_free(transaction->instructions); - // Free damage - list_foreach(transaction->damage, free); - list_free(transaction->damage); - if (transaction->timer) { wl_event_source_remove(transaction->timer); } @@ -174,13 +168,6 @@ void transaction_add_container(struct sway_transaction *transaction, list_add(transaction->instructions, instruction); } -void transaction_add_damage(struct sway_transaction *transaction, - struct wlr_box *_box) { - struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); - memcpy(box, _box, sizeof(struct wlr_box)); - list_add(transaction->damage, box); -} - /** * Apply a transaction to the "current" state of the tree. */ @@ -200,12 +187,32 @@ static void transaction_apply(struct sway_transaction *transaction) { "%.1fms total (%.1f frames if 60Hz)", transaction, ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); } + // Apply the instruction state to the container's current state for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *container = instruction->container; + // Damage the old and new locations + struct wlr_box old_box = { + .x = container->current.swayc_x, + .y = container->current.swayc_y, + .width = container->current.swayc_width, + .height = container->current.swayc_height, + }; + struct wlr_box new_box = { + .x = instruction->state.swayc_x, + .y = instruction->state.swayc_y, + .width = instruction->state.swayc_width, + .height = instruction->state.swayc_height, + }; + for (int j = 0; j < root_container.children->length; ++j) { + struct sway_container *output = root_container.children->items[j]; + output_damage_box(output->sway_output, &old_box); + output_damage_box(output->sway_output, &new_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. @@ -216,15 +223,6 @@ static void transaction_apply(struct sway_transaction *transaction) { memcpy(&container->current, &instruction->state, sizeof(struct sway_container_state)); } - - // Apply damage - for (int i = 0; i < transaction->damage->length; ++i) { - struct wlr_box *box = transaction->damage->items[i]; - for (int j = 0; j < root_container.children->length; ++j) { - struct sway_container *output = root_container.children->items[j]; - output_damage_box(output->sway_output, box); - } - } } static void transaction_progress_queue() { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 484afd0c..b6fa9525 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -272,10 +272,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(ws, transaction); - transaction_add_damage(transaction, container_get_box(ws->parent)); - transaction_commit(transaction); + arrange_and_commit(ws); } void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index da2eda7a..6042a806 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -267,10 +267,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(ws, transaction); - transaction_add_damage(transaction, container_get_box(ws->parent)); - transaction_commit(transaction); + arrange_and_commit(ws); } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 720ea2fd..1d5dab70 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -342,10 +342,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, xsurface->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(ws, transaction); - transaction_add_damage(transaction, container_get_box(ws->parent)); - transaction_commit(transaction); + arrange_and_commit(ws); } static void handle_set_title(struct wl_listener *listener, void *data) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index cb3f8ba2..582b2891 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -304,15 +304,6 @@ void arrange_windows(struct sway_container *container, case C_TYPES: break; } - // Add damage for whatever container arrange_windows() was called with, - // unless it was called with the special floating container, in which case - // we'll damage the entire output. - if (container->type == C_CONTAINER && container->layout == L_FLOATING) { - struct sway_container *output = container_parent(container, C_OUTPUT); - transaction_add_damage(transaction, container_get_box(output)); - } else { - transaction_add_damage(transaction, container_get_box(container)); - } add_deleted_containers(transaction); } diff --git a/sway/tree/view.c b/sway/tree/view.c index a616af03..68d2a029 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -550,11 +550,7 @@ void view_unmap(struct sway_view *view) { ws->sway_workspace->fullscreen = NULL; container_destroy(view->swayc); - struct sway_container *output = ws->parent; - struct sway_transaction *transaction = transaction_create(); - arrange_windows(output, transaction); - transaction_add_damage(transaction, container_get_box(output)); - transaction_commit(transaction); + arrange_and_commit(ws->parent); } else { struct sway_container *parent = container_destroy(view->swayc); arrange_and_commit(parent); -- cgit v1.2.3-54-g00ecf From 8773ed39701748ba5500b4698d028795aa6e812e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:47:41 +1000 Subject: Fix memleak in container_get_box Rather than allocate a structure and expect callers to free it, take a pointer to an existing struct as an argument. This function is no longer called anywhere though. --- include/sway/tree/container.h | 2 +- sway/tree/container.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 7e78cbef..728daa84 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -295,6 +295,6 @@ bool container_is_floating(struct sway_container *container); /** * Get a container's box in layout coordinates. */ -struct wlr_box *container_get_box(struct sway_container *container); +void container_get_box(struct sway_container *container, struct wlr_box *box); #endif diff --git a/sway/tree/container.c b/sway/tree/container.c index 8446c457..ab3d9dbd 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -988,11 +988,9 @@ bool container_is_floating(struct sway_container *container) { return container->parent == workspace->sway_workspace->floating; } -struct wlr_box *container_get_box(struct sway_container *container) { - struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); +void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->x; box->y = container->y; box->width = container->width; box->height = container->height; - return box; } -- cgit v1.2.3-54-g00ecf From 9652529cc161e943241d946dac93ab16d5e30ee5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 19:07:48 +1000 Subject: Allow views to skip configures To do this properly, the transaction queue will only be processed if it can be completely processed. --- common/list.c | 7 +++++++ include/list.h | 1 + include/sway/server.h | 2 +- sway/desktop/transaction.c | 51 +++++++++++++++++++++++++++------------------- sway/server.c | 3 +++ 5 files changed, 42 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/common/list.c b/common/list.c index 39cc10e1..cab10c1e 100644 --- a/common/list.c +++ b/common/list.c @@ -62,6 +62,13 @@ void list_cat(list_t *list, list_t *source) { } } +void list_empty(list_t *list) { + list->capacity = 10; + list->length = 0; + free(list->items); + list->items = malloc(sizeof(void*) * list->capacity); +} + void list_qsort(list_t *list, int compare(const void *left, const void *right)) { qsort(list->items, list->length, sizeof(void *), compare); } diff --git a/include/list.h b/include/list.h index 7eead4ac..d352dbd5 100644 --- a/include/list.h +++ b/include/list.h @@ -14,6 +14,7 @@ void list_add(list_t *list, void *item); void list_insert(list_t *list, int index, void *item); void list_del(list_t *list, int index); void list_cat(list_t *list, list_t *source); +void list_empty(list_t *list); // See qsort. Remember to use *_qsort functions as compare functions, // because they dereference the left and right arguments first! void list_qsort(list_t *list, int compare(const void *left, const void *right)); diff --git a/include/sway/server.h b/include/sway/server.h index bd96b085..0efc6baa 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,7 +47,7 @@ struct sway_server { bool debug_txn_timings; - struct sway_transaction *head_transaction; // singly linked list + list_t *transactions; // When a view is being destroyed and is waiting for a transaction to // complete it will be stored here. diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index c29b6661..7a2e78e5 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -32,7 +32,6 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * size_t num_waiting; size_t num_configures; - struct sway_transaction *next; struct timespec create_time; struct timespec commit_time; }; @@ -225,16 +224,24 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +/** + * For simplicity, we only progress the queue if it can be completely flushed. + */ static void transaction_progress_queue() { - struct sway_transaction *transaction = server.head_transaction; - struct sway_transaction *next = NULL; - while (transaction && !transaction->num_waiting) { - next = transaction->next; + // We iterate this list in reverse because we're more likely to find a + // waiting transactions at the end of the list. + for (int i = server.transactions->length - 1; i >= 0; --i) { + struct sway_transaction *transaction = server.transactions->items[i]; + if (transaction->num_waiting) { + return; + } + } + for (int i = 0; i < server.transactions->length; ++i) { + struct sway_transaction *transaction = server.transactions->items[i]; transaction_apply(transaction); transaction_destroy(transaction); - transaction = next; } - server.head_transaction = transaction; + list_empty(server.transactions); } static int handle_timeout(void *data) { @@ -295,18 +302,8 @@ void transaction_commit(struct sway_transaction *transaction) { if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } - if (server.head_transaction) { - // There is another transaction in progress - we must add this one to - // the queue so we complete after it. - struct sway_transaction *tail = server.head_transaction; - while (tail->next) { - tail = tail->next; - } - tail->next = transaction; - } else if (transaction->num_waiting) { - // There are no other transactions, but we're not applying immediately - // so we must jump in the queue so others will queue behind us. - server.head_transaction = transaction; + if (server.transactions->length || transaction->num_waiting) { + list_add(server.transactions, transaction); } else { // There are no other transactions in progress, and this one has nothing // to wait for, so we can skip the queue. @@ -359,12 +356,24 @@ static void set_instruction_ready( } } +/** + * Mark all of the view's instructions as ready up to and including the + * instruction at the given index. This allows the view to skip a configure. + */ +static void set_instructions_ready(struct sway_view *view, int index) { + for (int i = 0; i <= index; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + set_instruction_ready(instruction); + } +} + void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { for (int i = 0; i < view->swayc->instructions->length; ++i) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (instruction->serial == serial && !instruction->ready) { - set_instruction_ready(instruction); + set_instructions_ready(view, i); return; } } @@ -377,7 +386,7 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, view->swayc->instructions->items[i]; if (!instruction->ready && instruction->state.view_width == width && instruction->state.view_height == height) { - set_instruction_ready(instruction); + set_instructions_ready(view, i); return; } } diff --git a/sway/server.c b/sway/server.c index a2bc5702..884d971e 100644 --- a/sway/server.c +++ b/sway/server.c @@ -119,6 +119,8 @@ bool server_init(struct sway_server *server) { } server->destroying_containers = create_list(); + server->transactions = create_list(); + input_manager = input_manager_create(server); return true; } @@ -127,6 +129,7 @@ void server_fini(struct sway_server *server) { // TODO: free sway-specific resources wl_display_destroy(server->wl_display); list_free(server->destroying_containers); + list_free(server->transactions); } void server_run(struct sway_server *server) { -- cgit v1.2.3-54-g00ecf From d7169ee7ffd45318125dbe3013aadbd1482a3e5f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 19:44:54 +1000 Subject: Replace list_empty with a simple alternative --- common/list.c | 7 ------- include/list.h | 1 - sway/desktop/transaction.c | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) (limited to 'include') diff --git a/common/list.c b/common/list.c index cab10c1e..39cc10e1 100644 --- a/common/list.c +++ b/common/list.c @@ -62,13 +62,6 @@ void list_cat(list_t *list, list_t *source) { } } -void list_empty(list_t *list) { - list->capacity = 10; - list->length = 0; - free(list->items); - list->items = malloc(sizeof(void*) * list->capacity); -} - void list_qsort(list_t *list, int compare(const void *left, const void *right)) { qsort(list->items, list->length, sizeof(void *), compare); } diff --git a/include/list.h b/include/list.h index d352dbd5..7eead4ac 100644 --- a/include/list.h +++ b/include/list.h @@ -14,7 +14,6 @@ void list_add(list_t *list, void *item); void list_insert(list_t *list, int index, void *item); void list_del(list_t *list, int index); void list_cat(list_t *list, list_t *source); -void list_empty(list_t *list); // See qsort. Remember to use *_qsort functions as compare functions, // because they dereference the left and right arguments first! void list_qsort(list_t *list, int compare(const void *left, const void *right)); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7a2e78e5..fc23ef35 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -241,7 +241,7 @@ static void transaction_progress_queue() { transaction_apply(transaction); transaction_destroy(transaction); } - list_empty(server.transactions); + server.transactions->length = 0; } static int handle_timeout(void *data) { -- cgit v1.2.3-54-g00ecf From 3c81a900b766dd2c049ba7af6e603805893e0926 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 19:52:31 +1000 Subject: Add comment about usage to arrange_windows declaration --- include/sway/tree/arrange.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index 6c8c0dba..58235642 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -16,6 +16,10 @@ void add_gaps(struct sway_container *c); * * Use this function if you need to arrange multiple sections of the tree in one * transaction. + * + * You must set the desired state of the container before calling + * arrange_windows, then don't change any state-tracked properties in the + * container until you've called transaction_commit. */ void arrange_windows(struct sway_container *container, struct sway_transaction *transaction); -- cgit v1.2.3-54-g00ecf From 3a6ed5110c76ef5bed8cc4c26a97759f6201eaac Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 21:13:22 +1000 Subject: Render saved buffers with the surface's dimensions --- include/sway/desktop/transaction.h | 13 +++++++++---- sway/desktop/output.c | 8 +++++--- sway/desktop/transaction.c | 17 +++++++++++------ 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index fcfed297..7ab80eb8 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -52,11 +52,16 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, int width, int height); /** - * Get the texture that should be rendered for a view. + * Get the saved texture that should be rendered for a view. * - * In most cases this will return the normal live texture for a view, but if the - * view is in a transaction then it'll return a saved texture. + * The addresses pointed at by the width and height pointers will be populated + * with the surface's dimensions, which may be different to the texture's + * dimensions if output scaling is used. + * + * This function should only be called if it is known that the view has + * instructions. */ -struct wlr_texture *transaction_get_texture(struct sway_view *view); +struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, + int *width, int *height); #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 69d0bdd4..b55a3962 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -354,15 +354,17 @@ 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; - struct wlr_texture *texture = transaction_get_texture(view); + int width, height; + struct wlr_texture *texture = + transaction_get_saved_texture(view, &width, &height); if (!texture) { return; } struct wlr_box box = { .x = view->swayc->current.view_x - output->swayc->current.swayc_x, .y = view->swayc->current.view_y - output->swayc->current.swayc_y, - .width = view->swayc->current.view_width, - .height = view->swayc->current.view_height, + .width = width, + .height = height, }; struct wlr_box output_box = { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index fc23ef35..7c5a9b8f 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -41,6 +41,7 @@ struct sway_transaction_instruction { struct sway_container *container; struct sway_container_state state; struct wlr_buffer *saved_buffer; + int saved_buffer_width, saved_buffer_height; uint32_t serial; bool ready; }; @@ -71,6 +72,8 @@ static void save_view_buffer(struct sway_view *view, } if (view->surface && wlr_surface_has_buffer(view->surface)) { instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); + instruction->saved_buffer_width = view->surface->current->width; + instruction->saved_buffer_height = view->surface->current->height; } } @@ -392,12 +395,14 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, } } -struct wlr_texture *transaction_get_texture(struct sway_view *view) { - if (!view->swayc || !view->swayc->instructions->length) { - return view->surface->buffer->texture; - } +struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, + int *width, int *height) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[0]; - return instruction->saved_buffer ? - instruction->saved_buffer->texture : NULL; + if (!instruction->saved_buffer) { + return NULL; + } + *width = instruction->saved_buffer_width; + *height = instruction->saved_buffer_height; + return instruction->saved_buffer->texture; } -- cgit v1.2.3-54-g00ecf