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 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 include/sway/desktop/transaction.h (limited to 'include/sway/desktop/transaction.h') 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 -- cgit v1.2.3 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/sway/desktop/transaction.h') 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 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/sway/desktop/transaction.h') 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 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/sway/desktop/transaction.h') 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 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/sway/desktop/transaction.h') 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 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/sway/desktop/transaction.h') 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