From 59c94887018bdfa578c4371c4275061ca6e71b3e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 3 Jun 2018 16:35:06 +1000 Subject: [PATCH 01/34] WIP: Atomic layout updates ground work --- include/sway/desktop/transaction.h | 56 ++++ include/sway/output.h | 2 + include/sway/tree/arrange.h | 35 ++- include/sway/tree/container.h | 29 ++ include/sway/tree/view.h | 19 +- sway/desktop/output.c | 70 ++++- sway/desktop/transaction.c | 214 +++++++++++++ sway/desktop/xdg_shell.c | 29 +- sway/desktop/xdg_shell_v6.c | 30 +- sway/desktop/xwayland.c | 33 +- sway/meson.build | 1 + sway/tree/arrange.c | 481 +++++++++++++++-------------- sway/tree/container.c | 11 + sway/tree/layout.c | 4 +- sway/tree/view.c | 98 +++--- sway/tree/workspace.c | 6 + 16 files changed, 771 insertions(+), 347 deletions(-) create mode 100644 include/sway/desktop/transaction.h create mode 100644 sway/desktop/transaction.c diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h new file mode 100644 index 00000000..575d28c8 --- /dev/null +++ b/include/sway/desktop/transaction.h @@ -0,0 +1,56 @@ +#ifndef _SWAY_TRANSACTION_H +#define _SWAY_TRANSACTION_H +#include "sway/tree/container.h" + +/** + * Transactions enable us to perform atomic layout updates. + * + * When we want to make adjustments to the layout, we create a transaction. + * A transaction contains a list of affected containers and their new state. + * A state might contain a new size, or new border settings, or new parent/child + * relationships. + * + * Calling transaction_commit() makes sway notify of all the affected clients + * with their new sizes. We then wait for all the views to respond with their + * new surface sizes. When all are ready, or when a timeout has passed, we apply + * the updates all at the same time. + */ + +struct sway_transaction { + struct wl_event_source *timer; + list_t *instructions; // struct sway_transaction_instruction * + list_t *damage; // struct wlr_box * + size_t num_waiting; +}; + +/** + * Create a new transaction. + */ +struct sway_transaction *transaction_create(void); + +/** + * Add a container's pending state to the transaction. + */ +void transaction_add_container(struct sway_transaction *transaction, + struct sway_container *container); + +/** + * Add a box to be damaged when the transaction is applied. + * The box should be in layout coordinates. + */ +void transaction_add_damage(struct sway_transaction *transaction, + struct wlr_box *box); + +/** + * Submit a transaction to the client views for configuration. + */ +void transaction_commit(struct sway_transaction *transaction); + +/** + * Notify the transaction system that a view is ready for the new layout. + * + * When all views in the transaction are ready, the layout will be applied. + */ +void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); + +#endif diff --git a/include/sway/output.h b/include/sway/output.h index 70f746dc..e6ca0d02 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -42,6 +42,8 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, void output_damage_from_view(struct sway_output *output, struct sway_view *view); +void output_damage_box(struct sway_output *output, struct wlr_box *box); + void output_damage_whole_container(struct sway_output *output, struct sway_container *con); diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index ce95cfe9..23cd66dc 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -1,18 +1,33 @@ #ifndef _SWAY_ARRANGE_H #define _SWAY_ARRANGE_H +#include "sway/desktop/transaction.h" struct sway_container; -// Determine the root container's geometry, then iterate to everything below +/** + * Arrange layout for all the children of the given container, and add them to + * the given transaction. + * + * Use this function if you need to arrange multiple sections of the tree in one + * transaction. + */ +void arrange_windows(struct sway_container *container, + struct sway_transaction *transaction); + +/** + * Arrange layout for the given container and commit the transaction. + * + * This function is a wrapper around arrange_windows, and handles creating and + * committing the transaction for you. Use this function if you're only doing + * one arrange operation. + */ +void arrange_and_commit(struct sway_container *container); + +// These functions are temporary and are only here to make everything compile. +// They are wrappers around arrange_and_commit. void arrange_root(void); - -// Determine the output's geometry, then iterate to everything below -void arrange_output(struct sway_container *output); - -// Determine the workspace's geometry, then iterate to everything below -void arrange_workspace(struct sway_container *workspace); - -// Arrange layout for all the children of the given workspace/container -void arrange_children_of(struct sway_container *parent); +void arrange_output(struct sway_container *container); +void arrange_workspace(struct sway_container *container); +void arrange_children_of(struct sway_container *container); #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 7ed6aab1..dd5bd47c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -54,6 +54,28 @@ struct sway_output; struct sway_workspace; struct sway_view; +struct sway_container_state { + // Container/swayc properties + enum sway_container_layout layout; + double swayc_x, swayc_y; + double swayc_width, swayc_height; + + //struct sway_container *parent; + //list_t *children; + + // View properties + double view_x, view_y; + double view_width, view_height; + bool is_fullscreen; + + enum sway_container_border border; + int border_thickness; + bool border_top; + bool border_bottom; + bool border_left; + bool border_right; +}; + struct sway_container { union { // TODO: Encapsulate state for other node types as well like C_CONTAINER @@ -69,6 +91,8 @@ struct sway_container { */ size_t id; + struct sway_container_state pending; + char *name; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar @@ -246,4 +270,9 @@ void container_set_geometry_from_floating_view(struct sway_container *con); */ bool container_is_floating(struct sway_container *container); +/** + * Get a container's box in layout coordinates. + */ +struct wlr_box *container_get_box(struct sway_container *container); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 3df38e2d..f47db567 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -29,8 +29,8 @@ struct sway_view_impl { const char *(*get_string_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); - void (*configure)(struct sway_view *view, double lx, double ly, int width, - int height); + uint32_t (*configure)(struct sway_view *view, double lx, double ly, + int width, int height); void (*set_activated)(struct sway_view *view, bool activated); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); bool (*wants_floating)(struct sway_view *view); @@ -70,6 +70,12 @@ struct sway_view { list_t *executed_criteria; // struct criteria * list_t *marks; // char * + list_t *instructions; // struct sway_transaction_instruction * + + // If saved_texture is set, the main surface of the view will render this + // texture instead of its own. This is used while waiting for transactions + // to complete. + struct wlr_texture *saved_texture; struct wlr_texture *marks_focused; struct wlr_texture *marks_focused_inactive; @@ -103,8 +109,6 @@ struct sway_xdg_shell_v6_view { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - - int pending_width, pending_height; }; struct sway_xdg_shell_view { @@ -119,8 +123,6 @@ struct sway_xdg_shell_view { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - - int pending_width, pending_height; }; struct sway_xwayland_view { @@ -138,9 +140,6 @@ struct sway_xwayland_view { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; - - int pending_lx, pending_ly; - int pending_width, pending_height; }; struct sway_xwayland_unmanaged { @@ -212,7 +211,7 @@ uint32_t view_get_window_type(struct sway_view *view); const char *view_get_shell(struct sway_view *view); -void view_configure(struct sway_view *view, double ox, double oy, int width, +uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); /** diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3142bdb4..c5d445a6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -69,6 +69,7 @@ struct render_data { struct root_geometry root_geo; struct sway_output *output; pixman_region32_t *damage; + struct sway_view *view; float alpha; }; @@ -108,6 +109,38 @@ static bool get_surface_box(struct root_geometry *geo, 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->width; + int sh = view->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, + }; + + struct wlr_box intersection; + return wlr_box_intersection(&output_box, &rotated_box, &intersection); +} + static void surface_for_each_surface(struct wlr_surface *surface, double ox, double oy, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -225,13 +258,26 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, pixman_region32_t *output_damage = data->damage; float alpha = data->alpha; - if (!wlr_surface_has_buffer(surface)) { - return; + struct wlr_texture *texture = NULL; + struct wlr_box box; + bool intersects; + + // If this is the main surface of a view, render the saved_texture instead + // if it exists. It exists when we are mid-transaction. + if (data->view && data->view->saved_texture && + data->view->surface == surface) { + texture = data->view->saved_texture; + intersects = get_view_box(&data->root_geo, data->output, data->view, + sx, sy, &box); + } else { + if (!wlr_surface_has_buffer(surface)) { + return; + } + texture = surface->texture; + intersects = get_surface_box(&data->root_geo, data->output, surface, + sx, sy, &box); } - struct wlr_box box; - bool intersects = get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); if (!intersects) { return; } @@ -244,8 +290,7 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, wlr_matrix_project_box(matrix, &box, transform, rotation, wlr_output->transform_matrix); - render_texture(wlr_output, output_damage, surface->texture, &box, matrix, - alpha); + render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); } static void render_layer(struct sway_output *output, @@ -315,6 +360,7 @@ static void render_view_surfaces(struct sway_view *view, struct render_data data = { .output = output, .damage = damage, + .view = view, .alpha = alpha, }; output_view_for_each_surface( @@ -1134,6 +1180,16 @@ void output_damage_from_view(struct sway_output *output, output_damage_view(output, view, false); } +// Expecting an unscaled box in layout coordinates +void output_damage_box(struct sway_output *output, struct wlr_box *_box) { + struct wlr_box box; + memcpy(&box, _box, sizeof(struct wlr_box)); + box.x -= output->swayc->x; + box.y -= output->swayc->y; + scale_box(&box, output->wlr_output->scale); + wlr_output_damage_add_box(output->damage, &box); +} + static void output_damage_whole_container_iterator(struct sway_container *con, void *data) { struct sway_output *output = data; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c new file mode 100644 index 00000000..69f97e3d --- /dev/null +++ b/sway/desktop/transaction.c @@ -0,0 +1,214 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include "sway/debug.h" +#include "sway/desktop/transaction.h" +#include "sway/output.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "list.h" +#include "log.h" + +/** + * How long we should wait for views to respond to the configure before giving + * up and applying the transaction anyway. + */ +#define TIMEOUT_MS 200 + +struct sway_transaction_instruction { + struct sway_transaction *transaction; + struct sway_container *container; + struct sway_container_state state; + uint32_t serial; +}; + +struct sway_transaction *transaction_create() { + struct sway_transaction *transaction = + calloc(1, sizeof(struct sway_transaction)); + transaction->instructions = create_list(); + transaction->damage = create_list(); + return transaction; +} + +static void transaction_destroy(struct sway_transaction *transaction) { + int i; + // Free instructions + for (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; + } + } + } + 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_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; + memcpy(&instruction->state, &container->pending, + sizeof(struct sway_container_state)); + 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); +} + +static void save_view_texture(struct sway_view *view) { + wlr_texture_destroy(view->saved_texture); + view->saved_texture = NULL; + + // TODO: Copy the texture and store it in view->saved_texture. +} + +static void remove_saved_view_texture(struct sway_view *view) { + wlr_texture_destroy(view->saved_texture); + view->saved_texture = NULL; +} + +/** + * 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; + for (i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + struct sway_container_state *state = &instruction->state; + struct sway_container *container = instruction->container; + + container->layout = state->layout; + container->x = state->swayc_x; + container->y = state->swayc_y; + container->width = state->swayc_width; + container->height = state->swayc_height; + + if (container->type == C_VIEW) { + struct sway_view *view = container->sway_view; + view->x = state->view_x; + view->y = state->view_y; + view->width = state->view_width; + view->height = state->view_height; + view->is_fullscreen = state->is_fullscreen; + view->border = state->border; + view->border_thickness = state->border_thickness; + view->border_top = state->border_top; + view->border_left = state->border_left; + view->border_right = state->border_right; + view->border_bottom = state->border_bottom; + + remove_saved_view_texture(view); + } + } + + // 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) { + struct sway_container *output = root_container.children->items[j]; + output_damage_box(output->sway_output, box); + } + } + + update_debug_tree(); +} + +static int handle_timeout(void *data) { + struct sway_transaction *transaction = data; + wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting), applying anyway", + transaction, transaction->num_waiting); + transaction_apply(transaction); + transaction_destroy(transaction); + return 0; +} + +void transaction_commit(struct sway_transaction *transaction) { + wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", + transaction, transaction->instructions->length); + transaction->num_waiting = 0; + 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, + 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); + ++transaction->num_waiting; + } + } + } + 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); + transaction_destroy(transaction); + return; + } + + // Set up a timer which the views must respond within + transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_timeout, transaction); + wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); +} + +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) { + struct sway_transaction_instruction *tmp_instruction = + view->instructions->items[i]; + if (tmp_instruction->serial == serial) { + 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; + } + // If all views are ready, apply the transaction + struct sway_transaction *transaction = instruction->transaction; + if (--transaction->num_waiting == 0) { + wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); + wl_event_source_timer_update(transaction->timer, 0); + transaction_apply(transaction); + transaction_destroy(transaction); + } +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d2b8822c..f43a0a1b 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -5,6 +5,7 @@ #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" @@ -87,18 +88,14 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p } } -static void configure(struct sway_view *view, double lx, double ly, int width, - int height) { +static uint32_t configure(struct sway_view *view, double lx, double ly, + int width, int height) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { - return; + return 0; } - - xdg_shell_view->pending_width = width; - xdg_shell_view->pending_height = height; - wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); - view_update_position(view, lx, ly); + return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); } static void set_activated(struct sway_view *view, bool activated) { @@ -174,18 +171,12 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit); struct sway_view *view = &xdg_shell_view->view; - if (view->swayc && container_is_floating(view->swayc)) { - int width = view->wlr_xdg_surface->geometry.width; - int height = view->wlr_xdg_surface->geometry.height; - if (!width && !height) { - width = view->wlr_xdg_surface->surface->current->width; - height = view->wlr_xdg_surface->surface->current->height; - } - view_update_size(view, width, height); - } else { - view_update_size(view, xdg_shell_view->pending_width, - xdg_shell_view->pending_height); + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + + if (view->instructions->length) { + transaction_notify_view_ready(view, xdg_surface->configure_serial); } + view_update_title(view, false); view_damage_from(view); } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 6ffe334a..bce59174 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -3,6 +3,7 @@ #include #include #include +#include "sway/desktop/transaction.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/server.h" @@ -86,18 +87,15 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p } } -static void configure(struct sway_view *view, double lx, double ly, int width, - int height) { +static uint32_t configure(struct sway_view *view, double lx, double ly, + int width, int height) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { - return; + return 0; } - - xdg_shell_v6_view->pending_width = width; - xdg_shell_v6_view->pending_height = height; - wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height); - view_update_position(view, lx, ly); + return wlr_xdg_toplevel_v6_set_size( + view->wlr_xdg_surface_v6, width, height); } static void set_activated(struct sway_view *view, bool activated) { @@ -173,18 +171,12 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, commit); struct sway_view *view = &xdg_shell_v6_view->view; - if (view->swayc && container_is_floating(view->swayc)) { - int width = view->wlr_xdg_surface_v6->geometry.width; - int height = view->wlr_xdg_surface_v6->geometry.height; - if (!width && !height) { - width = view->wlr_xdg_surface_v6->surface->current->width; - height = view->wlr_xdg_surface_v6->surface->current->height; - } - view_update_size(view, width, height); - } else { - view_update_size(view, xdg_shell_v6_view->pending_width, - xdg_shell_v6_view->pending_height); + struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; + + if (view->instructions->length) { + transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } + view_update_title(view, false); view_damage_from(view); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6447b711..6a3c1b66 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -167,19 +167,18 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { } } -static void configure(struct sway_view *view, double lx, double ly, int width, +static uint32_t configure(struct sway_view *view, double lx, double ly, int width, int height) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { - return; + return 0; } struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - xwayland_view->pending_lx = lx; - xwayland_view->pending_ly = ly; - xwayland_view->pending_width = width; - xwayland_view->pending_height = height; wlr_xwayland_surface_configure(xsurface, lx, ly, width, height); + + // xwayland doesn't give us a serial for the configure + return 0; } static void set_activated(struct sway_view *view, bool activated) { @@ -250,15 +249,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, commit); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (view->swayc && container_is_floating(view->swayc)) { - view_update_size(view, xsurface->width, xsurface->height); - } else { - view_update_size(view, xwayland_view->pending_width, - xwayland_view->pending_height); + + // 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 && container_is_floating(view->swayc)) { + view_update_size(view, xsurface->width, xsurface->height); + } else { + view_update_size(view, view->swayc->pending.swayc_width, + view->swayc->pending.swayc_height); + } + view_update_position(view, + view->swayc->pending.view_x, view->swayc->pending.view_y); + view_damage_from(view); } - view_update_position(view, - xwayland_view->pending_lx, xwayland_view->pending_ly); - view_damage_from(view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/meson.build b/sway/meson.build index b6bb02a7..5ff62490 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -12,6 +12,7 @@ sway_sources = files( 'desktop/desktop.c', 'desktop/layer_shell.c', 'desktop/output.c', + 'desktop/transaction.c', 'desktop/xdg_shell_v6.c', 'desktop/xdg_shell.c', 'desktop/xwayland.c', diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 721b557e..d8b3aec1 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -5,7 +5,6 @@ #include #include #include -#include "sway/debug.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" @@ -17,7 +16,217 @@ struct sway_container root_container; -void arrange_root() { +static void apply_horiz_layout(struct sway_container *parent) { + size_t num_children = parent->children->length; + if (!num_children) { + return; + } + size_t parent_offset = 0; + if (parent->parent->pending.layout == L_TABBED) { + parent_offset = container_titlebar_height(); + } else if (parent->parent->pending.layout == L_STACKED) { + parent_offset = container_titlebar_height() * + parent->parent->children->length; + } + size_t parent_height = parent->pending.swayc_height - parent_offset; + + // Calculate total width of children + double total_width = 0; + for (size_t i = 0; i < num_children; ++i) { + struct sway_container *child = parent->children->items[i]; + if (child->pending.swayc_width <= 0) { + if (num_children > 1) { + child->pending.swayc_width = + parent->pending.swayc_width / (num_children - 1); + } else { + child->pending.swayc_width = parent->pending.swayc_width; + } + } + total_width += child->pending.swayc_width; + } + double scale = parent->pending.swayc_width / total_width; + + // Resize windows + wlr_log(L_DEBUG, "Arranging %p horizontally", parent); + double child_x = parent->pending.swayc_x; + for (size_t i = 0; i < num_children; ++i) { + struct sway_container *child = parent->children->items[i]; + wlr_log(L_DEBUG, + "Calculating arrangement for %p:%d (will scale %f by %f)", + child, child->type, child->pending.swayc_width, scale); + child->pending.swayc_x = child_x; + child->pending.swayc_y = parent->pending.swayc_y + parent_offset; + child->pending.swayc_width = floor(child->pending.swayc_width * scale); + child->pending.swayc_height = parent_height; + child_x += child->pending.swayc_width; + + // Make last child use remaining width of parent + if (i == num_children - 1) { + child->pending.swayc_width = parent->pending.swayc_x + + parent->pending.swayc_width - child->pending.swayc_x; + } + } +} + +static void apply_vert_layout(struct sway_container *parent) { + size_t num_children = parent->children->length; + if (!num_children) { + return; + } + size_t parent_offset = 0; + if (parent->parent->pending.layout == L_TABBED) { + parent_offset = container_titlebar_height(); + } else if (parent->parent->pending.layout == L_STACKED) { + parent_offset = + container_titlebar_height() * parent->parent->children->length; + } + size_t parent_height = parent->pending.swayc_height - parent_offset; + + // Calculate total height of children + double total_height = 0; + for (size_t i = 0; i < num_children; ++i) { + struct sway_container *child = parent->children->items[i]; + if (child->pending.swayc_height <= 0) { + if (num_children > 1) { + child->pending.swayc_height = + parent_height / (num_children - 1); + } else { + child->pending.swayc_height = parent_height; + } + } + total_height += child->pending.swayc_height; + } + double scale = parent_height / total_height; + + // Resize + wlr_log(L_DEBUG, "Arranging %p vertically", parent); + double child_y = parent->pending.swayc_y + parent_offset; + for (size_t i = 0; i < num_children; ++i) { + struct sway_container *child = parent->children->items[i]; + wlr_log(L_DEBUG, + "Calculating arrangement for %p:%d (will scale %f by %f)", + child, child->type, child->pending.swayc_height, scale); + child->pending.swayc_x = parent->pending.swayc_x; + child->pending.swayc_y = child_y; + child->pending.swayc_width = parent->pending.swayc_width; + child->pending.swayc_height = + floor(child->pending.swayc_height * scale); + child_y += child->pending.swayc_height; + + // Make last child use remaining height of parent + if (i == num_children - 1) { + child->pending.swayc_height = parent->pending.swayc_y + + parent_offset + parent_height - child->pending.swayc_y; + } + } +} + +static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { + if (!parent->children->length) { + return; + } + size_t parent_offset = 0; + if (parent->parent->pending.layout == L_TABBED) { + parent_offset = container_titlebar_height(); + } else if (parent->parent->pending.layout == L_STACKED) { + parent_offset = + container_titlebar_height() * parent->parent->children->length; + } + size_t parent_height = parent->pending.swayc_height - parent_offset; + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + child->pending.swayc_x = parent->pending.swayc_x; + child->pending.swayc_y = parent->pending.swayc_y + parent_offset; + child->pending.swayc_width = parent->pending.swayc_width; + child->pending.swayc_height = parent_height; + } +} + +static void _arrange_children_of(struct sway_container *parent, + struct sway_transaction *transaction) { + if (config->reloading) { + return; + } + wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, + parent->name, parent->pending.swayc_width, parent->pending.swayc_height, + parent->pending.swayc_x, parent->pending.swayc_y); + + // Calculate x, y, width and height of children + switch (parent->pending.layout) { + case L_HORIZ: + apply_horiz_layout(parent); + break; + case L_VERT: + apply_vert_layout(parent); + break; + case L_TABBED: + case L_STACKED: + apply_tabbed_or_stacked_layout(parent); + break; + case L_NONE: + apply_horiz_layout(parent); + break; + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + } + + // Recurse into child containers + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + if (child->type == C_VIEW) { + view_autoconfigure(child->sway_view); + } else { + _arrange_children_of(child, transaction); + } + transaction_add_container(transaction, child); + } +} + +static void _arrange_workspace(struct sway_container *workspace, + struct sway_transaction *transaction) { + if (config->reloading) { + return; + } + struct sway_container *output = workspace->parent; + struct wlr_box *area = &output->sway_output->usable_area; + wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", + area->width, area->height, area->x, area->y); + workspace->pending.swayc_width = area->width; + workspace->pending.swayc_height = area->height; + workspace->pending.swayc_x = output->x + area->x; + workspace->pending.swayc_y = output->y + area->y; + transaction_add_container(transaction, workspace); + wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, + workspace->pending.swayc_x, workspace->pending.swayc_y); + _arrange_children_of(workspace, transaction); +} + +static void _arrange_output(struct sway_container *output, + struct sway_transaction *transaction) { + if (config->reloading) { + return; + } + const struct wlr_box *output_box = wlr_output_layout_get_box( + root_container.sway_root->output_layout, + output->sway_output->wlr_output); + output->x = output_box->x; + output->y = output_box->y; + output->width = output_box->width; + output->height = output_box->height; + output->pending.swayc_x = output_box->x; + output->pending.swayc_y = output_box->y; + output->pending.swayc_width = output_box->width; + output->pending.swayc_height = output_box->height; + transaction_add_container(transaction, output); + wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", + output->name, output->pending.swayc_x, output->pending.swayc_y); + for (int i = 0; i < output->children->length; ++i) { + struct sway_container *workspace = output->children->items[i]; + _arrange_workspace(workspace, transaction); + } +} + +static void _arrange_root(struct sway_transaction *transaction) { if (config->reloading) { return; } @@ -29,236 +238,60 @@ void arrange_root() { root_container.y = layout_box->y; root_container.width = layout_box->width; root_container.height = layout_box->height; + root_container.pending.swayc_x = layout_box->x; + root_container.pending.swayc_y = layout_box->y; + root_container.pending.swayc_width = layout_box->width; + root_container.pending.swayc_height = layout_box->height; + transaction_add_container(transaction, &root_container); for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; - arrange_output(output); + _arrange_output(output, transaction); } } -void arrange_output(struct sway_container *output) { - if (config->reloading) { - return; - } - if (!sway_assert(output->type == C_OUTPUT, - "called arrange_output() on non-output container")) { - return; - } - const struct wlr_box *output_box = wlr_output_layout_get_box( - root_container.sway_root->output_layout, - output->sway_output->wlr_output); - output->x = output_box->x; - output->y = output_box->y; - output->width = output_box->width; - output->height = output_box->height; - wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", - 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); - } - container_damage_whole(output); -} - -void arrange_workspace(struct sway_container *workspace) { - if (config->reloading) { - return; - } - if (!sway_assert(workspace->type == C_WORKSPACE, - "called arrange_workspace() on non-workspace container")) { - return; - } - struct sway_container *output = workspace->parent; - struct wlr_box *area = &output->sway_output->usable_area; - wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", - area->width, area->height, area->x, area->y); - workspace->width = area->width; - workspace->height = area->height; - workspace->x = output->x + area->x; - workspace->y = output->y + area->y; - wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", - workspace->name, workspace->x, workspace->y); - arrange_children_of(workspace); - container_damage_whole(workspace); -} - -static void apply_horiz_layout(struct sway_container *parent) { - size_t num_children = parent->children->length; - if (!num_children) { - return; - } - size_t parent_offset = 0; - if (parent->parent->layout == L_TABBED) { - parent_offset = container_titlebar_height(); - } else if (parent->parent->layout == L_STACKED) { - parent_offset = - container_titlebar_height() * parent->parent->children->length; - } - size_t parent_height = parent->height - parent_offset; - - // Calculate total width of children - double total_width = 0; - for (size_t i = 0; i < num_children; ++i) { - struct sway_container *child = parent->children->items[i]; - if (child->width <= 0) { - if (num_children > 1) { - child->width = parent->width / (num_children - 1); - } else { - child->width = parent->width; - } - } - total_width += child->width; - } - double scale = parent->width / total_width; - - // Resize windows - wlr_log(L_DEBUG, "Arranging %p horizontally", parent); - double child_x = parent->x; - struct sway_container *child; - for (size_t i = 0; i < num_children; ++i) { - child = parent->children->items[i]; - wlr_log(L_DEBUG, - "Calculating arrangement for %p:%d (will scale %f by %f)", - child, child->type, child->width, scale); - child->x = child_x; - child->y = parent->y + parent_offset; - child->width = floor(child->width * scale); - child->height = parent_height; - child_x += child->width; - } - // Make last child use remaining width of parent - child->width = parent->x + parent->width - child->x; -} - -static void apply_vert_layout(struct sway_container *parent) { - size_t num_children = parent->children->length; - if (!num_children) { - return; - } - size_t parent_offset = 0; - if (parent->parent->layout == L_TABBED) { - parent_offset = container_titlebar_height(); - } else if (parent->parent->layout == L_STACKED) { - parent_offset = - container_titlebar_height() * parent->parent->children->length; - } - size_t parent_height = parent->height - parent_offset; - - // Calculate total height of children - double total_height = 0; - for (size_t i = 0; i < num_children; ++i) { - struct sway_container *child = parent->children->items[i]; - if (child->height <= 0) { - if (num_children > 1) { - child->height = parent_height / (num_children - 1); - } else { - child->height = parent_height; - } - } - total_height += child->height; - } - double scale = parent_height / total_height; - - // Resize - wlr_log(L_DEBUG, "Arranging %p vertically", parent); - double child_y = parent->y + parent_offset; - struct sway_container *child; - for (size_t i = 0; i < num_children; ++i) { - child = parent->children->items[i]; - wlr_log(L_DEBUG, - "Calculating arrangement for %p:%d (will scale %f by %f)", - child, child->type, child->height, scale); - child->x = parent->x; - child->y = child_y; - child->width = parent->width; - child->height = floor(child->height * scale); - child_y += child->height; - } - // Make last child use remaining height of parent - child->height = parent->y + parent_offset + parent_height - child->y; -} - -static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { - if (!parent->children->length) { - return; - } - size_t parent_offset = 0; - if (parent->parent->layout == L_TABBED) { - parent_offset = container_titlebar_height(); - } else if (parent->parent->layout == L_STACKED) { - parent_offset = - container_titlebar_height() * parent->parent->children->length; - } - size_t parent_height = parent->height - parent_offset; - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - child->x = parent->x; - child->y = parent->y + parent_offset; - child->width = parent->width; - child->height = parent_height; - } -} - -void arrange_children_of(struct sway_container *parent) { - if (config->reloading) { - return; - } - if (!sway_assert(parent->type == C_WORKSPACE || parent->type == C_CONTAINER, - "container is a %s", container_type_to_str(parent->type))) { - return; - } - - struct sway_container *workspace = parent; - if (workspace->type != C_WORKSPACE) { - workspace = container_parent(workspace, C_WORKSPACE); - } - if (workspace->sway_workspace->fullscreen) { - // Just arrange the fullscreen view and jump out - view_autoconfigure(workspace->sway_workspace->fullscreen); - return; - } - - wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, - parent->name, parent->width, parent->height, parent->x, parent->y); - - // Calculate x, y, width and height of children - switch (parent->layout) { - case L_HORIZ: - apply_horiz_layout(parent); +void arrange_windows(struct sway_container *container, + struct sway_transaction *transaction) { + switch (container->type) { + case C_ROOT: + _arrange_root(transaction); break; - case L_VERT: - apply_vert_layout(parent); + case C_OUTPUT: + _arrange_output(container, transaction); break; - case L_TABBED: - case L_STACKED: - apply_tabbed_or_stacked_layout(parent); + case C_WORKSPACE: + _arrange_workspace(container, transaction); break; - default: - wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); - apply_horiz_layout(parent); + case C_CONTAINER: + _arrange_children_of(container, transaction); + transaction_add_container(transaction, container); + break; + case C_VIEW: + break; + case C_TYPES: break; } - - // Apply x, y, width and height to children and recurse if needed - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - if (child->type == C_VIEW) { - view_autoconfigure(child->sway_view); - } else { - arrange_children_of(child); - } - } - - // If container is a workspace, process floating containers too - if (parent->type == C_WORKSPACE) { - struct sway_workspace *ws = workspace->sway_workspace; - for (int i = 0; i < ws->floating->children->length; ++i) { - struct sway_container *child = ws->floating->children->items[i]; - if (child->type != C_VIEW) { - arrange_children_of(child); - } - } - } - - container_damage_whole(parent); - update_debug_tree(); + transaction_add_damage(transaction, container_get_box(container)); +} + +void arrange_and_commit(struct sway_container *container) { + struct sway_transaction *transaction = transaction_create(); + 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 cd2c083c..e6956f5c 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -116,6 +116,7 @@ struct sway_container *container_create(enum sway_container_type type) { if (type != C_VIEW) { c->children = create_list(); + //c->pending.children = create_list(); } wl_signal_init(&c->events.destroy); @@ -162,6 +163,7 @@ static void _container_destroy(struct sway_container *cont) { wlr_texture_destroy(cont->title_urgent); list_free(cont->children); + //list_free(cont->pending.children); cont->children = NULL; free(cont); } @@ -971,3 +973,12 @@ bool container_is_floating(struct sway_container *container) { } return container->parent == workspace->sway_workspace->floating; } + +struct wlr_box *container_get_box(struct sway_container *container) { + struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); + box->x = container->x; + box->y = container->y; + box->width = container->width; + box->height = container->height; + return box; +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 6d4cd088..3bba049a 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -915,10 +915,10 @@ void container_recursive_resize(struct sway_container *container, bool layout_match = true; wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { - container->width += amount; + container->pending.swayc_width += amount; layout_match = container->layout == L_HORIZ; } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { - container->height += amount; + container->pending.swayc_height += amount; layout_match = container->layout == L_VERT; } if (container->children) { diff --git a/sway/tree/view.c b/sway/tree/view.c index c9c82405..40fe2740 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,6 +25,7 @@ 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); } @@ -37,6 +38,11 @@ void view_destroy(struct sway_view *view) { view_unmap(view); } + if (!sway_assert(view->instructions->length == 0, + "Tried to destroy view with pending instructions")) { + return; + } + list_free(view->executed_criteria); for (int i = 0; i < view->marks->length; ++i) { @@ -44,6 +50,8 @@ void view_destroy(struct sway_view *view) { } 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); @@ -119,11 +127,12 @@ const char *view_get_shell(struct sway_view *view) { return "unknown"; } -void view_configure(struct sway_view *view, double lx, double ly, int width, +uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height) { if (view->impl->configure) { - view->impl->configure(view, lx, ly, width, height); + return view->impl->configure(view, lx, ly, width, height); } + return 0; } static void view_autoconfigure_floating(struct sway_view *view) { @@ -178,21 +187,23 @@ void view_autoconfigure(struct sway_view *view) { } } - view->border_top = view->border_bottom = true; - view->border_left = view->border_right = true; + struct sway_container_state *state = &view->swayc->pending; + + state->border_top = state->border_bottom = true; + state->border_left = state->border_right = true; if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_VERTICAL || (config->hide_edge_borders == E_SMART && !other_views)) { - view->border_left = view->swayc->x != ws->x; - int right_x = view->swayc->x + view->swayc->width; - view->border_right = right_x != ws->x + ws->width; + state->border_left = state->swayc_x != ws->x; + int right_x = state->swayc_x + state->swayc_width; + state->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_HORIZONTAL || (config->hide_edge_borders == E_SMART && !other_views)) { - view->border_top = view->swayc->y != ws->y; - int bottom_y = view->swayc->y + view->swayc->height; - view->border_bottom = bottom_y != ws->y + ws->height; + state->border_top = state->swayc_y != ws->y; + int bottom_y = state->swayc_y + state->swayc_height; + state->border_bottom = bottom_y != ws->y + ws->height; } double x, y, width, height; @@ -202,53 +213,54 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the swayc's y is the top of the title // area. We have to offset the surface y by the height of the title bar, and // disable any top border because we'll always have the title bar. - if (view->swayc->parent->layout == L_TABBED) { + if (view->swayc->parent->pending.layout == L_TABBED) { y_offset = container_titlebar_height(); - view->border_top = false; - } else if (view->swayc->parent->layout == L_STACKED) { + state->border_top = false; + } else if (view->swayc->parent->pending.layout == L_STACKED) { y_offset = container_titlebar_height() * view->swayc->parent->children->length; view->border_top = false; } - switch (view->border) { + switch (state->border) { case B_NONE: - x = view->swayc->x; - y = view->swayc->y + y_offset; - width = view->swayc->width; - height = view->swayc->height - y_offset; + x = state->swayc_x; + y = state->swayc_y + y_offset; + width = state->swayc_width; + height = state->swayc_height - y_offset; break; case B_PIXEL: - x = view->swayc->x + view->border_thickness * view->border_left; - y = view->swayc->y + view->border_thickness * view->border_top + y_offset; - width = view->swayc->width - - view->border_thickness * view->border_left - - view->border_thickness * view->border_right; - height = view->swayc->height - y_offset - - view->border_thickness * view->border_top - - view->border_thickness * view->border_bottom; + x = state->swayc_x + state->border_thickness * state->border_left; + y = state->swayc_y + state->border_thickness * state->border_top + y_offset; + width = state->swayc_width + - state->border_thickness * state->border_left + - state->border_thickness * state->border_right; + height = state->swayc_height - y_offset + - state->border_thickness * state->border_top + - state->border_thickness * state->border_bottom; break; case B_NORMAL: // Height is: 1px border + 3px pad + title height + 3px pad + 1px border - x = view->swayc->x + view->border_thickness * view->border_left; - width = view->swayc->width - - view->border_thickness * view->border_left - - view->border_thickness * view->border_right; + x = state->swayc_x + state->border_thickness * state->border_left; + width = state->swayc_width + - state->border_thickness * state->border_left + - state->border_thickness * state->border_right; if (y_offset) { - y = view->swayc->y + y_offset; - height = view->swayc->height - y_offset - - view->border_thickness * view->border_bottom; + y = state->swayc_y + y_offset; + height = state->swayc_height - y_offset + - state->border_thickness * state->border_bottom; } else { - y = view->swayc->y + container_titlebar_height(); - height = view->swayc->height - container_titlebar_height() - - view->border_thickness * view->border_bottom; + y = state->swayc_y + container_titlebar_height(); + height = state->swayc_height - container_titlebar_height() + - state->border_thickness * state->border_bottom; } break; } - view->x = x; - view->y = y; - view_configure(view, x, y, width, height); + state->view_x = x; + state->view_y = y; + state->view_width = width; + state->view_height = height; } void view_set_activated(struct sway_view *view, bool activated) { @@ -307,8 +319,8 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { view_configure(view, view->saved_x, view->saved_y, view->saved_width, view->saved_height); } else { - view->swayc->width = view->swayc->saved_width; - view->swayc->height = view->swayc->saved_height; + view->swayc->width = view->swayc->saved_width; + view->swayc->height = view->swayc->saved_height; view_autoconfigure(view); } } @@ -496,6 +508,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->swayc = cont; view->border = config->border; view->border_thickness = config->border_thickness; + view->swayc->pending.border = config->border; + view->swayc->pending.border_thickness = config->border_thickness; view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -963,7 +977,7 @@ bool view_is_visible(struct sway_view *view) { } // Check the workspace is visible if (!is_sticky) { - return workspace_is_visible(workspace); + return workspace_is_visible(workspace); } return true; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 9ba210fd..ad581a96 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -60,6 +60,12 @@ struct sway_container *workspace_create(struct sway_container *output, workspace->prev_layout = L_NONE; workspace->layout = container_get_default_layout(output); + workspace->pending.swayc_x = workspace->x; + workspace->pending.swayc_y = workspace->y; + workspace->pending.swayc_width = workspace->width; + workspace->pending.swayc_height = workspace->height; + workspace->pending.layout = workspace->layout; + struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); if (!swayws) { return NULL; From f9e6d703d298dbdee0770fd9e0c64ab2d7ac7deb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Jun 2018 19:19:30 +1000 Subject: [PATCH 02/34] Make main properties be the pending state --- include/sway/tree/container.h | 4 +- sway/desktop/desktop.c | 3 +- sway/desktop/output.c | 28 ++++----- sway/desktop/transaction.c | 49 +++++++++------- sway/desktop/xwayland.c | 9 ++- sway/tree/arrange.c | 107 +++++++++++++++------------------- sway/tree/layout.c | 4 +- sway/tree/view.c | 90 ++++++++++++++-------------- sway/tree/workspace.c | 6 -- 9 files changed, 143 insertions(+), 157 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index dd5bd47c..a8efd893 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -91,7 +91,9 @@ struct sway_container { */ size_t id; - struct sway_container_state pending; + // The pending state is the main container properties, and the current state is in the below struct. + // This means most places of the code can refer to the main variables (pending state) and it'll just work. + struct sway_container_state current; char *name; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index 66f33151..e495790c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -7,7 +7,8 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *cont = root_container.children->items[i]; if (cont->type == C_OUTPUT) { - output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y, + output_damage_surface(cont->sway_output, + lx - cont->current.swayc_x, ly - cont->current.swayc_y, surface, whole); } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c5d445a6..8af05bc3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -774,9 +774,10 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = con->y + container_titlebar_height() * i; - render_titlebar(output, damage, child, child->x, y, child->width, - colors, title_texture, marks_texture); + int y = con->current.swayc_y + container_titlebar_height() * i; + render_titlebar(output, damage, child, child->current.swayc_x, y, + child->current.swayc_width, colors, + title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -795,7 +796,7 @@ static void render_container_stacked(struct sway_output *output, static void render_container(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - switch (con->layout) { + switch (con->current.layout) { case L_NONE: case L_HORIZ: case L_VERT: @@ -832,9 +833,10 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (con->sway_view->border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->x, con->y, con->width, - colors, title_texture, marks_texture); + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); } else { render_top_border(soutput, damage, con, colors); } @@ -1184,8 +1186,8 @@ void output_damage_from_view(struct sway_output *output, void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->swayc->x; - box.y -= output->swayc->y; + box.x -= output->swayc->current.swayc_x; + box.y -= output->swayc->current.swayc_y; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } @@ -1204,10 +1206,10 @@ static void output_damage_whole_container_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { struct wlr_box box = { - .x = con->x - output->wlr_output->lx, - .y = con->y - output->wlr_output->ly, - .width = con->width, - .height = con->height, + .x = con->current.swayc_x - output->wlr_output->lx, + .y = con->current.swayc_y - output->wlr_output->ly, + .width = con->current.swayc_width, + .height = con->current.swayc_height, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 69f97e3d..313e707b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -67,8 +67,30 @@ void transaction_add_container(struct sway_transaction *transaction, calloc(1, sizeof(struct sway_transaction_instruction)); instruction->transaction = transaction; instruction->container = container; - memcpy(&instruction->state, &container->pending, - sizeof(struct sway_container_state)); + + // Copy the container's main (pending) properties into the instruction state + struct sway_container_state *state = &instruction->state; + state->layout = container->layout; + state->swayc_x = container->x; + state->swayc_y = container->y; + state->swayc_width = container->width; + state->swayc_height = container->height; + + if (container->type == C_VIEW) { + struct sway_view *view = container->sway_view; + state->view_x = view->x; + state->view_y = view->y; + state->view_width = view->width; + state->view_height = view->height; + state->is_fullscreen = view->is_fullscreen; + state->border = view->border; + state->border_thickness = view->border_thickness; + state->border_top = view->border_top; + state->border_left = view->border_left; + state->border_right = view->border_right; + state->border_bottom = view->border_bottom; + } + list_add(transaction->instructions, instruction); } @@ -102,30 +124,13 @@ static void transaction_apply(struct sway_transaction *transaction) { for (i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - struct sway_container_state *state = &instruction->state; struct sway_container *container = instruction->container; - container->layout = state->layout; - container->x = state->swayc_x; - container->y = state->swayc_y; - container->width = state->swayc_width; - container->height = state->swayc_height; + memcpy(&instruction->container->current, &instruction->state, + sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - struct sway_view *view = container->sway_view; - view->x = state->view_x; - view->y = state->view_y; - view->width = state->view_width; - view->height = state->view_height; - view->is_fullscreen = state->is_fullscreen; - view->border = state->border; - view->border_thickness = state->border_thickness; - view->border_top = state->border_top; - view->border_left = state->border_left; - view->border_right = state->border_right; - view->border_bottom = state->border_bottom; - - remove_saved_view_texture(view); + remove_saved_view_texture(container->sway_view); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6a3c1b66..d8442530 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -257,11 +257,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { if (view->swayc && container_is_floating(view->swayc)) { view_update_size(view, xsurface->width, xsurface->height); } else { - view_update_size(view, view->swayc->pending.swayc_width, - view->swayc->pending.swayc_height); + view_update_size(view, view->swayc->width, view->swayc->height); } - view_update_position(view, - view->swayc->pending.view_x, view->swayc->pending.view_y); + view_update_position(view, view->x, view->y); view_damage_from(view); } } @@ -314,7 +312,8 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } // TODO: Let floating views do whatever - configure(view, view->swayc->x, view->swayc->y, view->width, view->height); + configure(view, view->swayc->current.view_x, view->swayc->current.view_y, + view->swayc->current.view_width, view->swayc->current.view_height); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index d8b3aec1..cf7ce61c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -22,48 +22,46 @@ static void apply_horiz_layout(struct sway_container *parent) { return; } size_t parent_offset = 0; - if (parent->parent->pending.layout == L_TABBED) { + if (parent->parent->layout == L_TABBED) { parent_offset = container_titlebar_height(); - } else if (parent->parent->pending.layout == L_STACKED) { + } else if (parent->parent->layout == L_STACKED) { parent_offset = container_titlebar_height() * parent->parent->children->length; } - size_t parent_height = parent->pending.swayc_height - parent_offset; + size_t parent_height = parent->height - parent_offset; // Calculate total width of children double total_width = 0; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; - if (child->pending.swayc_width <= 0) { + if (child->width <= 0) { if (num_children > 1) { - child->pending.swayc_width = - parent->pending.swayc_width / (num_children - 1); + child->width = parent->width / (num_children - 1); } else { - child->pending.swayc_width = parent->pending.swayc_width; + child->width = parent->width; } } - total_width += child->pending.swayc_width; + total_width += child->width; } - double scale = parent->pending.swayc_width / total_width; + double scale = parent->width / total_width; // Resize windows wlr_log(L_DEBUG, "Arranging %p horizontally", parent); - double child_x = parent->pending.swayc_x; + double child_x = parent->x; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; wlr_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", - child, child->type, child->pending.swayc_width, scale); - child->pending.swayc_x = child_x; - child->pending.swayc_y = parent->pending.swayc_y + parent_offset; - child->pending.swayc_width = floor(child->pending.swayc_width * scale); - child->pending.swayc_height = parent_height; - child_x += child->pending.swayc_width; + child, child->type, child->width, scale); + child->x = child_x; + child->y = parent->y + parent_offset; + child->width = floor(child->width * scale); + child->height = parent_height; + child_x += child->width; // Make last child use remaining width of parent if (i == num_children - 1) { - child->pending.swayc_width = parent->pending.swayc_x + - parent->pending.swayc_width - child->pending.swayc_x; + child->width = parent->x + parent->width - child->x; } } } @@ -74,49 +72,47 @@ static void apply_vert_layout(struct sway_container *parent) { return; } size_t parent_offset = 0; - if (parent->parent->pending.layout == L_TABBED) { + if (parent->parent->layout == L_TABBED) { parent_offset = container_titlebar_height(); - } else if (parent->parent->pending.layout == L_STACKED) { + } else if (parent->parent->layout == L_STACKED) { parent_offset = container_titlebar_height() * parent->parent->children->length; } - size_t parent_height = parent->pending.swayc_height - parent_offset; + size_t parent_height = parent->height - parent_offset; // Calculate total height of children double total_height = 0; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; - if (child->pending.swayc_height <= 0) { + if (child->height <= 0) { if (num_children > 1) { - child->pending.swayc_height = - parent_height / (num_children - 1); + child->height = parent_height / (num_children - 1); } else { - child->pending.swayc_height = parent_height; + child->height = parent_height; } } - total_height += child->pending.swayc_height; + total_height += child->height; } double scale = parent_height / total_height; // Resize wlr_log(L_DEBUG, "Arranging %p vertically", parent); - double child_y = parent->pending.swayc_y + parent_offset; + double child_y = parent->y + parent_offset; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; wlr_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", - child, child->type, child->pending.swayc_height, scale); - child->pending.swayc_x = parent->pending.swayc_x; - child->pending.swayc_y = child_y; - child->pending.swayc_width = parent->pending.swayc_width; - child->pending.swayc_height = - floor(child->pending.swayc_height * scale); - child_y += child->pending.swayc_height; + child, child->type, child->height, scale); + child->x = parent->x; + child->y = child_y; + child->width = parent->width; + child->height = floor(child->height * scale); + child_y += child->height; // Make last child use remaining height of parent if (i == num_children - 1) { - child->pending.swayc_height = parent->pending.swayc_y + - parent_offset + parent_height - child->pending.swayc_y; + child->height = + parent->y + parent_offset + parent_height - child->y; } } } @@ -126,19 +122,19 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { return; } size_t parent_offset = 0; - if (parent->parent->pending.layout == L_TABBED) { + if (parent->parent->layout == L_TABBED) { parent_offset = container_titlebar_height(); - } else if (parent->parent->pending.layout == L_STACKED) { + } else if (parent->parent->layout == L_STACKED) { parent_offset = container_titlebar_height() * parent->parent->children->length; } - size_t parent_height = parent->pending.swayc_height - parent_offset; + size_t parent_height = parent->height - parent_offset; for (int i = 0; i < parent->children->length; ++i) { struct sway_container *child = parent->children->items[i]; - child->pending.swayc_x = parent->pending.swayc_x; - child->pending.swayc_y = parent->pending.swayc_y + parent_offset; - child->pending.swayc_width = parent->pending.swayc_width; - child->pending.swayc_height = parent_height; + child->x = parent->x; + child->y = parent->y + parent_offset; + child->width = parent->width; + child->height = parent_height; } } @@ -148,11 +144,10 @@ static void _arrange_children_of(struct sway_container *parent, return; } wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, - parent->name, parent->pending.swayc_width, parent->pending.swayc_height, - parent->pending.swayc_x, parent->pending.swayc_y); + parent->name, parent->width, parent->height, parent->x, parent->y); // Calculate x, y, width and height of children - switch (parent->pending.layout) { + switch (parent->layout) { case L_HORIZ: apply_horiz_layout(parent); break; @@ -191,13 +186,13 @@ static void _arrange_workspace(struct sway_container *workspace, struct wlr_box *area = &output->sway_output->usable_area; wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", area->width, area->height, area->x, area->y); - workspace->pending.swayc_width = area->width; - workspace->pending.swayc_height = area->height; - workspace->pending.swayc_x = output->x + area->x; - workspace->pending.swayc_y = output->y + area->y; + workspace->width = area->width; + workspace->height = area->height; + workspace->x = output->x + area->x; + workspace->y = output->y + area->y; transaction_add_container(transaction, workspace); wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, - workspace->pending.swayc_x, workspace->pending.swayc_y); + workspace->x, workspace->y); _arrange_children_of(workspace, transaction); } @@ -213,13 +208,9 @@ static void _arrange_output(struct sway_container *output, output->y = output_box->y; output->width = output_box->width; output->height = output_box->height; - output->pending.swayc_x = output_box->x; - output->pending.swayc_y = output_box->y; - output->pending.swayc_width = output_box->width; - output->pending.swayc_height = output_box->height; transaction_add_container(transaction, output); wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", - output->name, output->pending.swayc_x, output->pending.swayc_y); + output->name, output->x, output->y); for (int i = 0; i < output->children->length; ++i) { struct sway_container *workspace = output->children->items[i]; _arrange_workspace(workspace, transaction); @@ -238,10 +229,6 @@ static void _arrange_root(struct sway_transaction *transaction) { root_container.y = layout_box->y; root_container.width = layout_box->width; root_container.height = layout_box->height; - root_container.pending.swayc_x = layout_box->x; - root_container.pending.swayc_y = layout_box->y; - root_container.pending.swayc_width = layout_box->width; - root_container.pending.swayc_height = layout_box->height; transaction_add_container(transaction, &root_container); for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 3bba049a..6d4cd088 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -915,10 +915,10 @@ void container_recursive_resize(struct sway_container *container, bool layout_match = true; wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { - container->pending.swayc_width += amount; + container->width += amount; layout_match = container->layout == L_HORIZ; } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { - container->pending.swayc_height += amount; + container->height += amount; layout_match = container->layout == L_VERT; } if (container->children) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 40fe2740..dbf803c6 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -187,23 +187,23 @@ void view_autoconfigure(struct sway_view *view) { } } - struct sway_container_state *state = &view->swayc->pending; + struct sway_container *con = view->swayc; - state->border_top = state->border_bottom = true; - state->border_left = state->border_right = true; + view->border_top = view->border_bottom = true; + view->border_left = view->border_right = true; if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_VERTICAL || (config->hide_edge_borders == E_SMART && !other_views)) { - state->border_left = state->swayc_x != ws->x; - int right_x = state->swayc_x + state->swayc_width; - state->border_right = right_x != ws->x + ws->width; + view->border_left = con->x != ws->x; + int right_x = con->x + con->width; + view->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_HORIZONTAL || (config->hide_edge_borders == E_SMART && !other_views)) { - state->border_top = state->swayc_y != ws->y; - int bottom_y = state->swayc_y + state->swayc_height; - state->border_bottom = bottom_y != ws->y + ws->height; + view->border_top = con->y != ws->y; + int bottom_y = con->y + con->height; + view->border_bottom = bottom_y != ws->y + ws->height; } double x, y, width, height; @@ -213,54 +213,53 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the swayc's y is the top of the title // area. We have to offset the surface y by the height of the title bar, and // disable any top border because we'll always have the title bar. - if (view->swayc->parent->pending.layout == L_TABBED) { + if (con->parent->layout == L_TABBED) { y_offset = container_titlebar_height(); - state->border_top = false; - } else if (view->swayc->parent->pending.layout == L_STACKED) { - y_offset = container_titlebar_height() - * view->swayc->parent->children->length; + view->border_top = false; + } else if (con->parent->layout == L_STACKED) { + y_offset = container_titlebar_height() * con->parent->children->length; view->border_top = false; } - switch (state->border) { + switch (view->border) { case B_NONE: - x = state->swayc_x; - y = state->swayc_y + y_offset; - width = state->swayc_width; - height = state->swayc_height - y_offset; + x = con->x; + y = con->y + y_offset; + width = con->width; + height = con->height - y_offset; break; case B_PIXEL: - x = state->swayc_x + state->border_thickness * state->border_left; - y = state->swayc_y + state->border_thickness * state->border_top + y_offset; - width = state->swayc_width - - state->border_thickness * state->border_left - - state->border_thickness * state->border_right; - height = state->swayc_height - y_offset - - state->border_thickness * state->border_top - - state->border_thickness * state->border_bottom; + x = con->x + view->border_thickness * view->border_left; + y = con->y + view->border_thickness * view->border_top + y_offset; + width = con->width + - view->border_thickness * view->border_left + - view->border_thickness * view->border_right; + height = con->height - y_offset + - view->border_thickness * view->border_top + - view->border_thickness * view->border_bottom; break; case B_NORMAL: // Height is: 1px border + 3px pad + title height + 3px pad + 1px border - x = state->swayc_x + state->border_thickness * state->border_left; - width = state->swayc_width - - state->border_thickness * state->border_left - - state->border_thickness * state->border_right; + x = con->x + view->border_thickness * view->border_left; + width = con->width + - view->border_thickness * view->border_left + - view->border_thickness * view->border_right; if (y_offset) { - y = state->swayc_y + y_offset; - height = state->swayc_height - y_offset - - state->border_thickness * state->border_bottom; + y = con->y + y_offset; + height = con->height - y_offset + - view->border_thickness * view->border_bottom; } else { - y = state->swayc_y + container_titlebar_height(); - height = state->swayc_height - container_titlebar_height() - - state->border_thickness * state->border_bottom; + y = con->y + container_titlebar_height(); + height = con->height - container_titlebar_height() + - view->border_thickness * view->border_bottom; } break; } - state->view_x = x; - state->view_y = y; - state->view_width = width; - state->view_height = height; + view->x = x; + view->y = y; + view->width = width; + view->height = height; } void view_set_activated(struct sway_view *view, bool activated) { @@ -319,9 +318,8 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { view_configure(view, view->saved_x, view->saved_y, view->saved_width, view->saved_height); } else { - view->swayc->width = view->swayc->saved_width; - view->swayc->height = view->swayc->saved_height; - view_autoconfigure(view); + view->swayc->width = view->swayc->saved_width; + view->swayc->height = view->swayc->saved_height; } } } @@ -508,8 +506,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->swayc = cont; view->border = config->border; view->border_thickness = config->border_thickness; - view->swayc->pending.border = config->border; - view->swayc->pending.border_thickness = config->border_thickness; view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -977,7 +973,7 @@ bool view_is_visible(struct sway_view *view) { } // Check the workspace is visible if (!is_sticky) { - return workspace_is_visible(workspace); + return workspace_is_visible(workspace); } return true; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index ad581a96..9ba210fd 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -60,12 +60,6 @@ struct sway_container *workspace_create(struct sway_container *output, workspace->prev_layout = L_NONE; workspace->layout = container_get_default_layout(output); - workspace->pending.swayc_x = workspace->x; - workspace->pending.swayc_y = workspace->y; - workspace->pending.swayc_width = workspace->width; - workspace->pending.swayc_height = workspace->height; - workspace->pending.layout = workspace->layout; - struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); if (!swayws) { return NULL; From bb66e6d578fdc68fb33d0fde921390d74f20bb31 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Jun 2018 22:57:34 +1000 Subject: [PATCH 03/34] 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(-) 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; } From 1c89f32533534f6e78c81c95578f40df45bb9016 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Jun 2018 20:42:12 +1000 Subject: [PATCH 04/34] Preserve buffers during transactions * Also fix parts of the rendering where it was rendering the pending state instead of current. --- include/sway/tree/view.h | 9 ++-- sway/desktop/output.c | 101 +++++++++++++++++++------------------ sway/desktop/transaction.c | 28 ++++++---- sway/desktop/xwayland.c | 2 +- sway/tree/container.c | 3 -- 5 files changed, 76 insertions(+), 67 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index d0093db5..fc4c8df9 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -72,10 +72,11 @@ struct sway_view { list_t *marks; // char * list_t *instructions; // struct sway_transaction_instruction * - // If saved_texture is set, the main surface of the view will render this - // texture instead of its own. This is used while waiting for transactions - // to complete. - struct wlr_texture *saved_texture; + // If saved_buffer is set, the main surface of the view will render this + // buffer/texture instead of its own. This is used while waiting for + // transactions to complete. + struct wlr_buffer *saved_buffer; + int saved_surface_width, saved_surface_height; struct wlr_texture *marks_focused; struct wlr_texture *marks_focused_inactive; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 37528cac..a485cb10 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -112,8 +113,8 @@ static bool get_surface_box(struct root_geometry *geo, static bool get_view_box(struct root_geometry *geo, struct sway_output *output, struct sway_view *view, int sx, int sy, struct wlr_box *surface_box) { - int sw = view->width; - int sh = view->height; + int sw = view->saved_surface_width; + int sh = view->saved_surface_height; double _sx = sx, _sy = sy; rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, @@ -157,10 +158,10 @@ static void output_view_for_each_surface(struct sway_view *view, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct render_data *data = user_data; - geo->x = view->x - data->output->swayc->x; - geo->y = view->y - data->output->swayc->y; - geo->width = view->surface->current->width; - geo->height = view->surface->current->height; + geo->x = view->swayc->current.view_x - data->output->swayc->x; + geo->y = view->swayc->current.view_y - data->output->swayc->y; + geo->width = view->swayc->current.view_width; + geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO view_for_each_surface(view, iterator, user_data); @@ -277,11 +278,11 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, struct wlr_box box; bool intersects; - // If this is the main surface of a view, render the saved_texture instead + // If this is the main surface of a view, render the saved_buffer instead // if it exists. It exists when we are mid-transaction. - if (data->view && data->view->saved_texture && + if (data->view && data->view->saved_buffer && data->view->surface == surface) { - texture = data->view->saved_texture; + texture = data->view->saved_buffer->texture; intersects = get_view_box(&data->root_geo, data->output, data->view, sx, sy, &box); } else { @@ -405,46 +406,46 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, float output_scale = output->wlr_output->scale; float color[4]; - if (view->border != B_NONE) { - if (view->border_left) { + if (con->current.border != B_NONE) { + if (con->current.border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = view->y; - box.width = view->border_thickness; - box.height = view->height; + box.x = con->current.swayc_x; + box.y = con->current.view_y; + box.width = con->current.border_thickness; + box.height = con->current.view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (view->border_right) { + if (con->current.border_right) { if (con->parent->children->length == 1 - && con->parent->layout == L_HORIZ) { + && con->parent->current.layout == L_HORIZ) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = view->x + view->width; - box.y = view->y; - box.width = view->border_thickness; - box.height = view->height; + box.x = con->current.view_x + con->current.view_width; + box.y = con->current.view_y; + box.width = con->current.border_thickness; + box.height = con->current.view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (view->border_bottom) { + if (con->current.border_bottom) { if (con->parent->children->length == 1 - && con->parent->layout == L_VERT) { + && con->parent->current.layout == L_VERT) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = view->y + view->height; - box.width = con->width; - box.height = view->border_thickness; + box.x = con->current.swayc_x; + box.y = con->current.view_y + con->current.view_height; + box.width = con->current.swayc_width; + box.height = con->current.border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -468,9 +469,8 @@ static void render_titlebar(struct sway_output *output, struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; - struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL; float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = con->parent->layout; + enum sway_container_layout layout = con->parent->current.layout; bool is_last_child = con->parent->children->items[con->parent->children->length - 1] == con; @@ -489,9 +489,11 @@ static void render_titlebar(struct sway_output *output, bool connects_sides = false; if (layout == L_HORIZ || layout == L_VERT || (layout == L_STACKED && is_last_child)) { - if (view) { - left_offset = view->border_left * view->border_thickness; - right_offset = view->border_right * view->border_thickness; + if (con->type == C_VIEW) { + left_offset = + con->current.border_left * con->current.border_thickness; + right_offset = + con->current.border_right * con->current.border_thickness; connects_sides = true; } } @@ -612,15 +614,16 @@ static void render_titlebar(struct sway_output *output, // Left pixel in line with bottom bar box.x = x; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = view->border_thickness * view->border_left; + box.width = con->current.border_thickness * con->current.border_left; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Right pixel in line with bottom bar - box.x = x + width - view->border_thickness * view->border_right; + box.x = x + width - + con->current.border_thickness * con->current.border_right; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = view->border_thickness * view->border_right; + box.width = con->current.border_thickness * con->current.border_right; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -633,8 +636,7 @@ static void render_titlebar(struct sway_output *output, static void render_top_border(struct sway_output *output, pixman_region32_t *output_damage, struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->sway_view; - if (!view->border_top) { + if (!con->current.border_top) { return; } struct wlr_box box; @@ -644,10 +646,10 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y; - box.width = con->width; - box.height = view->border_thickness; + box.x = con->current.swayc_x; + box.y = con->current.swayc_y; + box.width = con->current.swayc_width; + box.height = con->current.border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } @@ -688,9 +690,10 @@ static void render_container_simple(struct sway_output *output, marks_texture = child->sway_view->marks_unfocused; } - if (child->sway_view->border == B_NORMAL) { - render_titlebar(output, damage, child, child->x, child->y, - child->width, colors, title_texture, marks_texture); + if (child->current.border == B_NORMAL) { + render_titlebar(output, damage, child, child->current.swayc_x, + child->current.swayc_y, child->current.swayc_width, + colors, title_texture, marks_texture); } else { render_top_border(output, damage, child, colors); } @@ -739,15 +742,15 @@ static void render_container_tabbed(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int tab_width = con->width / con->children->length; - int x = con->x + tab_width * i; + int tab_width = con->current.swayc_width / con->children->length; + int x = con->current.swayc_x + tab_width * i; // Make last tab use the remaining width of the parent if (i == con->children->length - 1) { - tab_width = con->width - tab_width * i; + tab_width = con->current.swayc_width - tab_width * i; } - render_titlebar(output, damage, child, x, child->y, tab_width, colors, - title_texture, marks_texture); + render_titlebar(output, damage, child, x, child->current.swayc_y, + tab_width, colors, title_texture, marks_texture); if (child == current) { current_colors = colors; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 07bfbf7a..77377a18 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "sway/debug.h" #include "sway/desktop/transaction.h" @@ -112,16 +113,23 @@ void transaction_add_damage(struct sway_transaction *transaction, list_add(transaction->damage, box); } -static void save_view_texture(struct sway_view *view) { - wlr_texture_destroy(view->saved_texture); - view->saved_texture = NULL; - - // TODO: Copy the texture and store it in view->saved_texture. +static void save_view_buffer(struct sway_view *view) { + if (view->saved_buffer) { + wlr_buffer_unref(view->saved_buffer); + } + wlr_buffer_ref(view->surface->buffer); + view->saved_buffer = view->surface->buffer; + view->saved_surface_width = view->surface->current->width; + view->saved_surface_height = view->surface->current->height; } -static void remove_saved_view_texture(struct sway_view *view) { - wlr_texture_destroy(view->saved_texture); - view->saved_texture = NULL; +static void remove_saved_view_buffer(struct sway_view *view) { + if (view->saved_buffer) { + wlr_buffer_unref(view->saved_buffer); + view->saved_buffer = NULL; + view->saved_surface_width = 0; + view->saved_surface_height = 0; + } } /** @@ -141,7 +149,7 @@ static void transaction_apply(struct sway_transaction *transaction) { sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - remove_saved_view_texture(container->sway_view); + remove_saved_view_buffer(container->sway_view); } } @@ -183,7 +191,7 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_texture(con->sway_view); + save_view_buffer(con->sway_view); list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 70929d48..55917bf6 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -261,8 +261,8 @@ static void handle_commit(struct wl_listener *listener, void *data) { view_update_size(view, view->swayc->width, view->swayc->height); } view_update_position(view, view->x, view->y); - view_damage_from(view); } + view_damage_from(view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index f8620b72..b071f394 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -766,9 +766,6 @@ static void update_title_texture(struct sway_container *con, "Unexpected type %s", container_type_to_str(con->type))) { return; } - if (!con->width) { - return; - } struct sway_container *output = container_parent(con, C_OUTPUT); if (!output) { return; From 38398e2d77d57dc06b67ec88a54091c897915602 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 23 Jun 2018 16:24:11 +1000 Subject: [PATCH 05/34] 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 | 188 +++++++++--------- sway/tree/layout.c | 2 + sway/tree/output.c | 2 - sway/tree/view.c | 78 ++++---- sway/tree/workspace.c | 3 + 18 files changed, 545 insertions(+), 389 deletions(-) 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; - view_unmap(&xdg_shell_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_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) { +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); +} + +void container_free(struct sway_container *cont) { + if (!sway_assert(cont->destroying, + "Tried to free container which wasn't marked as destroying")) { return; } - - wl_signal_emit(&cont->events.destroy, cont); - - 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 (!sway_assert(cont->instructions->length == 0, + "Tried to free container with pending instructions")) { + return; } - if (cont->marks) { - list_foreach(cont->marks, free); - list_free(cont->marks); - } - if (parent) { - parent = container_remove_child(cont); - } - if (cont->name) { - free(cont->name); - } - + 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; } - - struct sway_container *parent = con->parent; - - 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; + if (con->destroying) { + return NULL; } + // 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; + } + } + + // 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->instructions->length == 0, - "Tried to destroy view with pending instructions")) { + if (!sway_assert(view->destroying, + "Tried to free view which wasn't marked as destroying")) { + return; + } + 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); From 32b865e610dd937af17ce36b8c986e41f55a4627 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 23 Jun 2018 17:47:28 +1000 Subject: [PATCH 06/34] Fix crash when deleting last child in a tabbed or stacked container There was no `current` child because the container was destroyed. This makes it fall back to looking in the parent's current children list. --- include/sway/input/seat.h | 11 +++++++++++ sway/desktop/output.c | 28 ++++++++++++++++------------ sway/input/seat.c | 12 ++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 1f7792ba..0e440701 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -118,6 +118,17 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *seat_get_active_child(struct sway_seat *seat, struct sway_container *container); +/** + * Return the immediate child of container which was most recently focused, with + * fallback to selecting the child in the parent's `current` (rendered) children + * list. + * + * This is useful for when a tabbed container and its children are destroyed but + * still being rendered, and we have to render an appropriate child. + */ +struct sway_container *seat_get_active_current_child(struct sway_seat *seat, + struct sway_container *container); + /** * Iterate over the focus-inactive children of the container calling the * function on each. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9db95ef5..1ca48975 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -713,7 +713,7 @@ static void render_container_tabbed(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_child(seat, con); + struct sway_container *current = seat_get_active_current_child(seat, con); struct border_colors *current_colors = NULL; struct sway_container_state *pstate = &con->current; @@ -756,11 +756,13 @@ static void render_container_tabbed(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } } @@ -775,7 +777,7 @@ static void render_container_stacked(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_child(seat, con); + struct sway_container *current = seat_get_active_current_child(seat, con); struct border_colors *current_colors = NULL; struct sway_container_state *pstate = &con->current; @@ -812,11 +814,13 @@ static void render_container_stacked(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 1ea36466..436d18e2 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -829,6 +829,18 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat, return NULL; } +struct sway_container *seat_get_active_current_child(struct sway_seat *seat, + struct sway_container *container) { + struct sway_container *child = seat_get_active_child(seat, container); + if (child) { + return child; + } + if (container->current.children->length == 1) { + return container->current.children->items[0]; + } + return NULL; +} + struct sway_container *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; From f08a30d6d04b5f986ea1e66a017e81bcd7c77e39 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 12:33:23 +1000 Subject: [PATCH 07/34] Force transactions to complete in order This forces transactions to complete in order by using a singly linked list stored in the sway server. --- include/sway/server.h | 2 + sway/desktop/transaction.c | 90 ++++++++++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index b07e86a7..2aa7b7fe 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,6 +47,8 @@ struct sway_server { bool terminating; + struct sway_transaction *head_transaction; // singly linked list + // When a view is being destroyed and is waiting for a transaction to // complete it will be stored here. list_t *destroying_containers; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 6e09537a..04142bcc 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -31,6 +31,7 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * list_t *damage; // struct wlr_box * size_t num_waiting; + struct sway_transaction *next; }; struct sway_transaction_instruction { @@ -175,6 +176,7 @@ void transaction_add_damage(struct sway_transaction *transaction, * Apply a transaction to the "current" state of the tree. */ static void transaction_apply(struct sway_transaction *transaction) { + wlr_log(L_DEBUG, "Applying transaction %p", transaction); int i; // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { @@ -203,15 +205,54 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +static void progress_queue() { + struct sway_transaction *transaction = server.head_transaction; + struct sway_transaction *next = NULL; + while (transaction && !transaction->num_waiting) { + next = transaction->next; + transaction_apply(transaction); + transaction_destroy(transaction); + transaction = next; + } + server.head_transaction = transaction; +} + static int handle_timeout(void *data) { struct sway_transaction *transaction = data; - wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting), applying anyway", + wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", transaction, transaction->num_waiting); - transaction_apply(transaction); - transaction_destroy(transaction); + transaction->num_waiting = 0; + progress_queue(); return 0; } +static bool should_configure(struct sway_container *con, + struct sway_transaction_instruction *instruction) { + if (con->type != C_VIEW) { + return false; + } + if (con->destroying) { + return false; + } + // The settled dimensions are what size the view will be once any pending + // configures have applied (excluding the one we might be configuring now). + // If these match the dimensions that this transaction wants then we don't + // need to configure it. + int settled_width = con->current.view_width; + int settled_height = con->current.view_height; + if (con->instructions->length) { + struct sway_transaction_instruction *last_instruction = + con->instructions->items[con->instructions->length - 1]; + settled_width = last_instruction->state.view_width; + settled_height = last_instruction->state.view_height; + } + if (settled_width == instruction->state.view_width && + settled_height == instruction->state.view_height) { + return false; + } + return true; +} + void transaction_commit(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", transaction, transaction->instructions->length); @@ -220,9 +261,7 @@ void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *con = instruction->container; - if (con->type == C_VIEW && !con->destroying && - (con->current.view_width != instruction->state.view_width || - con->current.view_height != instruction->state.view_height)) { + if (should_configure(con, instruction)) { instruction->serial = view_configure(con->sway_view, instruction->state.view_x, instruction->state.view_y, @@ -234,18 +273,33 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } - if (!transaction->num_waiting) { - wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", - transaction); + if (server.head_transaction) { + // There is another transaction in progress - we must add this one to + // the queue so we complete after it. + struct sway_transaction *tail = server.head_transaction; + while (tail->next) { + tail = tail->next; + } + tail->next = transaction; + } else if (transaction->num_waiting) { + // There are no other transactions, but we're not applying immediately + // so we must jump in the queue so others will queue behind us. + server.head_transaction = transaction; + } else { + // There are no other transactions in progress, and this one has nothing + // to wait for, so we can skip the queue. + wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); transaction_apply(transaction); transaction_destroy(transaction); return; } - // Set up a timer which the views must respond within - transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, - handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + if (transaction->num_waiting) { + // Set up a timer which the views must respond within + transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_timeout, transaction); + wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + } // The debug tree shows the pending/live tree. Here is a good place to // update it, because we make a transaction every time we change the pending @@ -269,14 +323,14 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { } instruction->ready = true; - // If all views are ready, apply the transaction + // If all views are ready, apply the transaction. + // If the transaction has timed out then its num_waiting will be 0 already. struct sway_transaction *transaction = instruction->transaction; - if (--transaction->num_waiting == 0) { + if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { #if !TRANSACTION_DEBUG - wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); + wlr_log(L_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - transaction_apply(transaction); - transaction_destroy(transaction); + progress_queue(); #endif } } From 33e03cb27795b267bcd3c7508363ec8f95127e95 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 13:08:47 +1000 Subject: [PATCH 08/34] Fix crash related to stacks and tabs --- sway/desktop/output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1ca48975..790751c3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -714,7 +714,7 @@ static void render_container_tabbed(struct sway_output *output, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = NULL; + struct border_colors *current_colors = &config->border_colors.unfocused; struct sway_container_state *pstate = &con->current; // Render tabs @@ -778,7 +778,7 @@ static void render_container_stacked(struct sway_output *output, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = NULL; + struct border_colors *current_colors = &config->border_colors.unfocused; struct sway_container_state *pstate = &con->current; // Render titles From b864ac0149212adf753824366e20badfa971b29f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 15:50:53 +1000 Subject: [PATCH 09/34] Fix crash when unmapping a view with reapable parents container_destroy was calling container_reap_empty, which calls container_destroy and so on. Eventually the original container_destroy would return a NULL pointer to the caller which caused a crash. This also fixes an arrange on the wrong container when moving views in and out of stacks. --- sway/commands/move.c | 14 +++++--- sway/tree/container.c | 82 +++++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 2c9fb77a..da0f89e9 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -177,13 +177,19 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, static void move_in_direction(struct sway_container *container, enum wlr_direction direction, int move_amt) { - struct sway_container *old_parent = container->parent; + // For simplicity, we'll arrange the entire workspace. The reason for this + // is moving the container might reap the old parent, and container_move + // does not return a surviving parent. + // TODO: Make container_move return the surviving parent so we can arrange + // just that. + struct sway_container *old_ws = container_parent(container, C_WORKSPACE); container_move(container, direction, move_amt); + struct sway_container *new_ws = container_parent(container, C_WORKSPACE); struct sway_transaction *txn = transaction_create(); - arrange_windows(old_parent, txn); - if (container->parent != old_parent) { - arrange_windows(container->parent, txn); + arrange_windows(old_ws, txn); + if (new_ws != old_ws) { + arrange_windows(new_ws, txn); } transaction_commit(txn); } diff --git a/sway/tree/container.c b/sway/tree/container.c index 484d26a5..075c508c 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -293,6 +293,47 @@ static struct sway_container *container_output_destroy( return &root_container; } +/** + * Implement the actual destroy logic, without reaping. + */ +struct sway_container *container_destroy_noreaping(struct sway_container *con) { + if (con == NULL) { + return NULL; + } + if (con->destroying) { + return NULL; + } + + // 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)) { + wlr_log(L_ERROR, "workspace doesn't want to destroy"); + return NULL; + } + } + + // 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"); + + con->destroying = true; + list_add(server.destroying_containers, con); + + return container_remove_child(con); +} + bool container_reap_empty(struct sway_container *con) { if (con->layout == L_FLOATING) { // Don't reap the magical floating container that each workspace has @@ -306,13 +347,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_destroy(con); + container_destroy_noreaping(con); return true; } break; case C_CONTAINER: if (con->children->length == 0) { - container_destroy(con); + container_destroy_noreaping(con); return true; } case C_VIEW: @@ -354,42 +395,15 @@ struct sway_container *container_flatten(struct sway_container *container) { * 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(). + * + * This function just wraps container_destroy_noreaping(), then does reaping. */ struct sway_container *container_destroy(struct sway_container *con) { - if (con == NULL) { + struct sway_container *parent = container_destroy_noreaping(con); + + if (!parent) { return NULL; } - if (con->destroying) { - return NULL; - } - - // 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; - } - } - - // 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); } From b6a238c7b70bfb6520c55c480bf6a7e60b4f7db4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 16:03:24 +1000 Subject: [PATCH 10/34] Fix crash when running move in an empty workspace --- sway/commands/move.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index da0f89e9..4ce8d089 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -175,8 +175,12 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static void move_in_direction(struct sway_container *container, +static struct cmd_results *move_in_direction(struct sway_container *container, enum wlr_direction direction, int move_amt) { + if (container->type == C_WORKSPACE) { + return cmd_results_new(CMD_FAILURE, "move", + "Cannot move workspaces in a direction"); + } // For simplicity, we'll arrange the entire workspace. The reason for this // is moving the container might reap the old parent, and container_move // does not return a surviving parent. @@ -192,6 +196,8 @@ static void move_in_direction(struct sway_container *container, arrange_windows(new_ws, txn); } transaction_commit(txn); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } struct cmd_results *cmd_move(int argc, char **argv) { @@ -212,13 +218,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { } if (strcasecmp(argv[0], "left") == 0) { - move_in_direction(current, MOVE_LEFT, move_amt); + return move_in_direction(current, MOVE_LEFT, move_amt); } else if (strcasecmp(argv[0], "right") == 0) { - move_in_direction(current, MOVE_RIGHT, move_amt); + return move_in_direction(current, MOVE_RIGHT, move_amt); } else if (strcasecmp(argv[0], "up") == 0) { - move_in_direction(current, MOVE_UP, move_amt); + return move_in_direction(current, MOVE_UP, move_amt); } else if (strcasecmp(argv[0], "down") == 0) { - move_in_direction(current, MOVE_DOWN, move_amt); + return 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); From 1549fb719ae75a498bf319db45281464e72c759e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 23:01:09 +1000 Subject: [PATCH 11/34] 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(-) 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); } From a3976e2659ec3a90ba606ca5a93cfa8e78c410e1 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 23:07:52 +1000 Subject: [PATCH 12/34] Fix another crash when moving out of stacks or tabs --- sway/tree/container.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 075c508c..e30c7839 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -331,6 +331,10 @@ struct sway_container *container_destroy_noreaping(struct sway_container *con) { con->destroying = true; list_add(server.destroying_containers, con); + if (!con->parent) { + return NULL; + } + return container_remove_child(con); } @@ -384,7 +388,7 @@ struct sway_container *container_flatten(struct sway_container *container) { struct sway_container *child = container->children->items[0]; struct sway_container *parent = container->parent; container_replace_child(container, child); - container_destroy(container); + container_destroy_noreaping(container); container = parent; } return container; From 289d696adc22a4247345cb544454bac0dfd5d828 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 09:09:43 +1000 Subject: [PATCH 13/34] Implement transaction timings debug Launch sway with SWAY_DEBUG=txn_timings to enable it. --- include/sway/server.h | 2 ++ sway/desktop/transaction.c | 23 +++++++++++++++++++++++ sway/server.c | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/include/sway/server.h b/include/sway/server.h index 2aa7b7fe..94e8f2a2 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -45,6 +45,8 @@ struct sway_server { struct wlr_wl_shell *wl_shell; struct wl_listener wl_shell_surface; + bool debug_txn_timings; + bool terminating; struct sway_transaction *head_transaction; // singly linked list diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 08678b5b..cb23ab69 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "sway/debug.h" @@ -32,6 +33,8 @@ struct sway_transaction { list_t *damage; // struct wlr_box * size_t num_waiting; struct sway_transaction *next; + struct timespec create_time; + struct timespec commit_time; }; struct sway_transaction_instruction { @@ -48,6 +51,9 @@ struct sway_transaction *transaction_create() { calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); transaction->damage = create_list(); + if (server.debug_txn_timings) { + clock_gettime(CLOCK_MONOTONIC, &transaction->create_time); + } return transaction; } @@ -177,6 +183,20 @@ void transaction_add_damage(struct sway_transaction *transaction, */ static void transaction_apply(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Applying transaction %p", transaction); + if (server.debug_txn_timings) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec *create = &transaction->create_time; + struct timespec *commit = &transaction->commit_time; + float ms_arranging = (commit->tv_sec - create->tv_sec) * 1000 + + (commit->tv_nsec - create->tv_nsec) / 1000000.0; + float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + + (now.tv_nsec - commit->tv_nsec) / 1000000.0; + float ms_total = ms_arranging + ms_waiting; + wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " + "%.1fms total (%.1f frames if 60hz)", transaction, + ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); + } int i; // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { @@ -271,6 +291,9 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } + if (server.debug_txn_timings) { + clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); + } if (server.head_transaction) { // There is another transaction in progress - we must add this one to // the queue so we complete after it. diff --git a/sway/server.c b/sway/server.c index 86d4a643..a2bc5702 100644 --- a/sway/server.c +++ b/sway/server.c @@ -113,6 +113,10 @@ bool server_init(struct sway_server *server) { return false; } + const char *debug = getenv("SWAY_DEBUG"); + if (debug != NULL && strcmp(debug, "txn_timings") == 0) { + server->debug_txn_timings = true; + } server->destroying_containers = create_list(); input_manager = input_manager_create(server); From c371ff3de8abbaf3428eadb905d7f940281196c1 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 09:25:51 +1000 Subject: [PATCH 14/34] Implement per-configure debug timings --- sway/desktop/transaction.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index cb23ab69..31a9bf57 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -32,6 +32,7 @@ struct sway_transaction { 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; struct timespec create_time; struct timespec commit_time; @@ -291,6 +292,7 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } + transaction->num_configures = transaction->num_waiting; if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } @@ -331,10 +333,24 @@ void transaction_commit(struct sway_transaction *transaction) { static void set_instruction_ready( struct sway_transaction_instruction *instruction) { instruction->ready = true; + struct sway_transaction *transaction = instruction->transaction; + + if (server.debug_txn_timings) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec *start = &transaction->commit_time; + float ms = (now.tv_sec - start->tv_sec) * 1000 + + (now.tv_nsec - start->tv_nsec) / 1000000.0; + wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", + transaction, + transaction->num_configures - transaction->num_waiting + 1, + transaction->num_configures, ms, + instruction->container->name); + + } // If all views are ready, apply the transaction. // If the transaction has timed out then its num_waiting will be 0 already. - struct sway_transaction *transaction = instruction->transaction; if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { #if !TRANSACTION_DEBUG wlr_log(L_DEBUG, "Transaction %p is ready", transaction); From 9b15e81cff62eb214c89f62bc9e499c7f21d86cf Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 16:41:31 +1000 Subject: [PATCH 15/34] Fix potential crash when fullscreen view unmaps It happened when a view is a grandchild or deeper of the workspace, is fullscreen, and unmaps. The workspace would not be included in the transaction and its pointer to the fullscreen view was left dangling. --- sway/tree/view.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index cb36f123..2ca0dbbb 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -542,14 +542,16 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { struct sway_container *view_unmap(struct sway_view *view) { wl_signal_emit(&view->events.unmap, view); + wl_list_remove(&view->surface_new_subsurface.link); + wl_list_remove(&view->container_reparent.link); + if (view->is_fullscreen) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); ws->sway_workspace->fullscreen = NULL; + container_destroy(view->swayc); + return ws; } - wl_list_remove(&view->surface_new_subsurface.link); - wl_list_remove(&view->container_reparent.link); - return container_destroy(view->swayc); } From beacd4d9f9c6da7459bcde0e95031dac41387a7c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 16:50:01 +1000 Subject: [PATCH 16/34] Rename progress_queue to transaction_progress_queue --- sway/desktop/transaction.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 31a9bf57..7727ec6e 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -226,7 +226,7 @@ static void transaction_apply(struct sway_transaction *transaction) { } } -static void progress_queue() { +static void transaction_progress_queue() { struct sway_transaction *transaction = server.head_transaction; struct sway_transaction *next = NULL; while (transaction && !transaction->num_waiting) { @@ -243,7 +243,7 @@ static int handle_timeout(void *data) { wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", transaction, transaction->num_waiting); transaction->num_waiting = 0; - progress_queue(); + transaction_progress_queue(); return 0; } @@ -355,7 +355,7 @@ static void set_instruction_ready( #if !TRANSACTION_DEBUG wlr_log(L_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - progress_queue(); + transaction_progress_queue(); #endif } } From 7a922c65aab27c5f4282cf15de52d240e5ac8052 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 13:15:45 +1000 Subject: [PATCH 17/34] Damage output when a fullscreen view unmaps Also moved the arranging into view_unmap to avoid excessive code duplication. --- include/sway/tree/view.h | 5 +---- sway/desktop/xdg_shell.c | 3 +-- sway/desktop/xdg_shell_v6.c | 3 +-- sway/desktop/xwayland.c | 7 ++----- sway/tree/view.c | 14 ++++++++++---- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5a615b43..0e6f5292 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -245,10 +245,7 @@ void view_destroy(struct sway_view *view); void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); -/** - * Unmap the view and return the surviving parent (after reaping). - */ -struct sway_container *view_unmap(struct sway_view *view); +void view_unmap(struct sway_view *view); void view_update_position(struct sway_view *view, double lx, double ly); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ab35b98f..a06c3bd2 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -197,8 +197,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 76c1fa24..424bca7b 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -196,8 +196,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7e78ef32..53fa42cc 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -260,8 +260,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xwayland_view->commit.link); view->surface = NULL; @@ -297,9 +296,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_view *view = &xwayland_view->view; if (view->surface) { - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); - + view_unmap(view); wl_list_remove(&xwayland_view->commit.link); view->surface = NULL; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 2ca0dbbb..5a78112a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -539,7 +539,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_handle_container_reparent(&view->container_reparent, NULL); } -struct sway_container *view_unmap(struct sway_view *view) { +void view_unmap(struct sway_view *view) { wl_signal_emit(&view->events.unmap, view); wl_list_remove(&view->surface_new_subsurface.link); @@ -549,10 +549,16 @@ struct sway_container *view_unmap(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); ws->sway_workspace->fullscreen = NULL; container_destroy(view->swayc); - return ws; - } - return container_destroy(view->swayc); + struct sway_container *output = ws->parent; + struct sway_transaction *transaction = transaction_create(); + arrange_windows(output, transaction); + transaction_add_damage(transaction, container_get_box(output)); + transaction_commit(transaction); + } else { + struct sway_container *parent = container_destroy(view->swayc); + arrange_and_commit(parent); + } } void view_update_position(struct sway_view *view, double lx, double ly) { From 50190bc7609d981c45d26cd0b7d6d0fbf66feb05 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 13:18:33 +1000 Subject: [PATCH 18/34] Rename view's free callback to destroy --- include/sway/tree/view.h | 2 +- sway/desktop/xdg_shell.c | 4 ++-- sway/desktop/xdg_shell_v6.c | 4 ++-- sway/desktop/xwayland.c | 4 ++-- sway/tree/view.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0e6f5292..1bcb0582 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -37,7 +37,7 @@ struct sway_view_impl { void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); - void (*free)(struct sway_view *view); + void (*destroy)(struct sway_view *view); }; struct sway_view { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index a06c3bd2..105e77ae 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -143,7 +143,7 @@ static void _close(struct sway_view *view) { } } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { @@ -160,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 424bca7b..19b30604 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -143,7 +143,7 @@ static void _close(struct sway_view *view) { } } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { @@ -160,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 53fa42cc..eea8420d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -219,7 +219,7 @@ static void _close(struct sway_view *view) { wlr_xwayland_surface_close(view->wlr_xwayland_surface); } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return; @@ -235,7 +235,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 5a78112a..a616af03 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -51,8 +51,8 @@ void view_free(struct sway_view *view) { wlr_texture_destroy(view->marks_unfocused); wlr_texture_destroy(view->marks_urgent); - if (view->impl->free) { - view->impl->free(view); + if (view->impl->destroy) { + view->impl->destroy(view); } else { free(view); } From e8001e6fbe827f6ae6842cf9f221edb322bb570e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 14:24:15 +1000 Subject: [PATCH 19/34] Damage output when views toggle fullscreen Also add workspace to the transaction when a view maps in fullscreen mode. --- sway/desktop/xdg_shell.c | 10 ++++++++-- sway/desktop/xdg_shell_v6.c | 10 ++++++++-- sway/desktop/xwayland.c | 12 ++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 105e77ae..484afd0c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -221,8 +221,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); + } else { + arrange_and_commit(view->swayc->parent); } - arrange_and_commit(view->swayc->parent); xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, @@ -269,7 +272,10 @@ 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); - arrange_and_commit(ws); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(ws, transaction); + transaction_add_damage(transaction, container_get_box(ws->parent)); + transaction_commit(transaction); } 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 19b30604..da2eda7a 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -220,8 +220,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); + } else { + arrange_and_commit(view->swayc->parent); } - arrange_and_commit(view->swayc->parent); xdg_shell_v6_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, @@ -264,7 +267,10 @@ 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); - arrange_and_commit(ws); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(ws, transaction); + transaction_add_damage(transaction, container_get_box(ws->parent)); + transaction_commit(transaction); } 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 eea8420d..720ea2fd 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -286,8 +286,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); + } else { + arrange_and_commit(view->swayc->parent); } - arrange_and_commit(view->swayc->parent); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -337,7 +340,12 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } view_set_fullscreen(view, xsurface->fullscreen); - arrange_and_commit(view->swayc); + + 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); } static void handle_set_title(struct wl_listener *listener, void *data) { From 0085f64ac073666aa661938f15e8c1190d69a69b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 18:51:37 +1000 Subject: [PATCH 20/34] Remove timer when transaction destroys --- sway/desktop/transaction.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7727ec6e..7a99bfe2 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -102,6 +102,9 @@ static void transaction_destroy(struct sway_transaction *transaction) { list_foreach(transaction->damage, free); list_free(transaction->damage); + if (transaction->timer) { + wl_event_source_remove(transaction->timer); + } free(transaction); } From 834805f5e260bcc77d714323d4a7f4bfd1dbfb17 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 19:40:42 +1000 Subject: [PATCH 21/34] Fix crash when disconnecting output We were freeing the sway_output immediately upon disconnect which left a dangling pointer in the output's container. It then tried to use the pointer when the container is freed. We don't need to store the sway_output in an output's container which is destroying, so the fix is to set the pointer to NULL and remove the use in container_free. Also added an arrange when the output is disconnected for good measure. --- sway/desktop/output.c | 2 ++ sway/tree/container.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 790751c3..34fefaa9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1271,6 +1271,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->destroy.link); output->wlr_output->data = NULL; free(output); + + arrange_and_commit(&root_container); } static void handle_mode(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index e30c7839..2f041a36 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -171,7 +171,6 @@ void container_free(struct sway_container *cont) { case C_ROOT: break; case C_OUTPUT: - cont->sway_output->swayc = NULL; break; case C_WORKSPACE: container_workspace_free(cont->sway_workspace); @@ -287,6 +286,7 @@ static struct sway_container *container_output_destroy( wl_list_remove(&output->sway_output->damage_frame.link); output->sway_output->swayc = NULL; + output->sway_output = NULL; wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); From 93696b78ecbc31ec34be97ec26836efb74d359f0 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 20:14:58 +1000 Subject: [PATCH 22/34] Fix crash when closing output window from outer session Emitting the close event needs to happen before container_output_destroy, because container_output_destroy sets the sway_output to NULL and sway_output is used in IPC. --- sway/tree/container.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 2f041a36..0c860405 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -304,6 +304,9 @@ struct sway_container *container_destroy_noreaping(struct sway_container *con) { return NULL; } + wl_signal_emit(&con->events.destroy, con); + ipc_event_window(con, "close"); + // The below functions move their children to somewhere else. if (con->type == C_OUTPUT) { container_output_destroy(con); @@ -325,9 +328,6 @@ struct sway_container *container_destroy_noreaping(struct sway_container *con) { } } - wl_signal_emit(&con->events.destroy, con); - ipc_event_window(con, "close"); - con->destroying = true; list_add(server.destroying_containers, con); From a7b3f29292cad029f010aa8b5fafb56b08ba4ed7 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 20:18:57 +1000 Subject: [PATCH 23/34] Remove incorrect assertion and supporting code Children can exist when destroying a container, such as when destroying the last output. Sway is not terminating in that case. --- include/sway/server.h | 2 -- sway/main.c | 1 - sway/tree/container.c | 9 --------- 3 files changed, 12 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 94e8f2a2..bd96b085 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,8 +47,6 @@ struct sway_server { bool debug_txn_timings; - bool terminating; - struct sway_transaction *head_transaction; // singly linked list // When a view is being destroyed and is waiting for a transaction to diff --git a/sway/main.c b/sway/main.c index 61ae6a5f..a325dc3a 100644 --- a/sway/main.c +++ b/sway/main.c @@ -34,7 +34,6 @@ struct sway_server server; void sway_terminate(int exit_code) { terminate_request = true; exit_value = exit_code; - server.terminating = true; wl_display_terminate(server.wl_display); } diff --git a/sway/tree/container.c b/sway/tree/container.c index 0c860405..2b9eb636 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -319,15 +319,6 @@ struct sway_container *container_destroy_noreaping(struct sway_container *con) { } } - // At this point the container being destroyed shouldn't have any children - // unless sway is terminating. - if (!server.terminating) { - if (!sway_assert(!con->children || con->children->length == 0, - "Didn't expect to see children here")) { - return NULL; - } - } - con->destroying = true; list_add(server.destroying_containers, con); From 61c118768564eec07ac16494d90f567e75ea60cf Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:23:44 +1000 Subject: [PATCH 24/34] Fix nitpicks --- sway/desktop/transaction.c | 10 ++++------ sway/tree/container.c | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7a99bfe2..98cde889 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -73,8 +73,7 @@ static void save_view_buffer(struct sway_view *view, 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; + instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); } } @@ -198,12 +197,11 @@ static void transaction_apply(struct sway_transaction *transaction) { (now.tv_nsec - commit->tv_nsec) / 1000000.0; float ms_total = ms_arranging + ms_waiting; wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " - "%.1fms total (%.1f frames if 60hz)", transaction, + "%.1fms total (%.1f frames if 60Hz)", transaction, ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); } - int i; // Apply the instruction state to the container's current state - for (i = 0; i < transaction->instructions->length; ++i) { + for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *container = instruction->container; @@ -220,7 +218,7 @@ static void transaction_apply(struct sway_transaction *transaction) { } // Apply damage - for (i = 0; i < transaction->damage->length; ++i) { + 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]; diff --git a/sway/tree/container.c b/sway/tree/container.c index 2b9eb636..8446c457 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -296,7 +296,8 @@ static struct sway_container *container_output_destroy( /** * Implement the actual destroy logic, without reaping. */ -struct sway_container *container_destroy_noreaping(struct sway_container *con) { +static struct sway_container *container_destroy_noreaping( + struct sway_container *con) { if (con == NULL) { return NULL; } From be86d3aba602fef7b51fafa8a6e7a39d1e49817f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:46:03 +1000 Subject: [PATCH 25/34] 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(-) 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); From 8773ed39701748ba5500b4698d028795aa6e812e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:47:41 +1000 Subject: [PATCH 26/34] Fix memleak in container_get_box Rather than allocate a structure and expect callers to free it, take a pointer to an existing struct as an argument. This function is no longer called anywhere though. --- include/sway/tree/container.h | 2 +- sway/tree/container.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 7e78cbef..728daa84 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -295,6 +295,6 @@ bool container_is_floating(struct sway_container *container); /** * Get a container's box in layout coordinates. */ -struct wlr_box *container_get_box(struct sway_container *container); +void container_get_box(struct sway_container *container, struct wlr_box *box); #endif diff --git a/sway/tree/container.c b/sway/tree/container.c index 8446c457..ab3d9dbd 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -988,11 +988,9 @@ bool container_is_floating(struct sway_container *container) { return container->parent == workspace->sway_workspace->floating; } -struct wlr_box *container_get_box(struct sway_container *container) { - struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); +void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->x; box->y = container->y; box->width = container->width; box->height = container->height; - return box; } From e6829c5991cac1bd164f800c14fccd522d702783 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:54:57 +1000 Subject: [PATCH 27/34] Move unsetting of view->surface into view_unmap --- sway/desktop/xdg_shell.c | 1 - sway/desktop/xdg_shell_v6.c | 1 - sway/desktop/xwayland.c | 2 -- sway/tree/view.c | 1 + 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index b6fa9525..b076d772 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -201,7 +201,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { 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) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 6042a806..7320e629 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -200,7 +200,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { 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) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 1d5dab70..854da006 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -263,7 +263,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); wl_list_remove(&xwayland_view->commit.link); - view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -301,7 +300,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (view->surface) { view_unmap(view); wl_list_remove(&xwayland_view->commit.link); - view->surface = NULL; } wl_list_remove(&xwayland_view->destroy.link); diff --git a/sway/tree/view.c b/sway/tree/view.c index 68d2a029..9f85bac0 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -555,6 +555,7 @@ void view_unmap(struct sway_view *view) { struct sway_container *parent = container_destroy(view->swayc); arrange_and_commit(parent); } + view->surface = NULL; } void view_update_position(struct sway_view *view, double lx, double ly) { From 9652529cc161e943241d946dac93ab16d5e30ee5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 19:07:48 +1000 Subject: [PATCH 28/34] Allow views to skip configures To do this properly, the transaction queue will only be processed if it can be completely processed. --- common/list.c | 7 ++++++ include/list.h | 1 + include/sway/server.h | 2 +- sway/desktop/transaction.c | 51 ++++++++++++++++++++++---------------- sway/server.c | 3 +++ 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/common/list.c b/common/list.c index 39cc10e1..cab10c1e 100644 --- a/common/list.c +++ b/common/list.c @@ -62,6 +62,13 @@ void list_cat(list_t *list, list_t *source) { } } +void list_empty(list_t *list) { + list->capacity = 10; + list->length = 0; + free(list->items); + list->items = malloc(sizeof(void*) * list->capacity); +} + void list_qsort(list_t *list, int compare(const void *left, const void *right)) { qsort(list->items, list->length, sizeof(void *), compare); } diff --git a/include/list.h b/include/list.h index 7eead4ac..d352dbd5 100644 --- a/include/list.h +++ b/include/list.h @@ -14,6 +14,7 @@ void list_add(list_t *list, void *item); void list_insert(list_t *list, int index, void *item); void list_del(list_t *list, int index); void list_cat(list_t *list, list_t *source); +void list_empty(list_t *list); // See qsort. Remember to use *_qsort functions as compare functions, // because they dereference the left and right arguments first! void list_qsort(list_t *list, int compare(const void *left, const void *right)); diff --git a/include/sway/server.h b/include/sway/server.h index bd96b085..0efc6baa 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,7 +47,7 @@ struct sway_server { bool debug_txn_timings; - struct sway_transaction *head_transaction; // singly linked list + list_t *transactions; // When a view is being destroyed and is waiting for a transaction to // complete it will be stored here. diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index c29b6661..7a2e78e5 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -32,7 +32,6 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * size_t num_waiting; size_t num_configures; - struct sway_transaction *next; struct timespec create_time; struct timespec commit_time; }; @@ -225,16 +224,24 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +/** + * For simplicity, we only progress the queue if it can be completely flushed. + */ static void transaction_progress_queue() { - struct sway_transaction *transaction = server.head_transaction; - struct sway_transaction *next = NULL; - while (transaction && !transaction->num_waiting) { - next = transaction->next; + // We iterate this list in reverse because we're more likely to find a + // waiting transactions at the end of the list. + for (int i = server.transactions->length - 1; i >= 0; --i) { + struct sway_transaction *transaction = server.transactions->items[i]; + if (transaction->num_waiting) { + return; + } + } + for (int i = 0; i < server.transactions->length; ++i) { + struct sway_transaction *transaction = server.transactions->items[i]; transaction_apply(transaction); transaction_destroy(transaction); - transaction = next; } - server.head_transaction = transaction; + list_empty(server.transactions); } static int handle_timeout(void *data) { @@ -295,18 +302,8 @@ void transaction_commit(struct sway_transaction *transaction) { if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } - if (server.head_transaction) { - // There is another transaction in progress - we must add this one to - // the queue so we complete after it. - struct sway_transaction *tail = server.head_transaction; - while (tail->next) { - tail = tail->next; - } - tail->next = transaction; - } else if (transaction->num_waiting) { - // There are no other transactions, but we're not applying immediately - // so we must jump in the queue so others will queue behind us. - server.head_transaction = transaction; + if (server.transactions->length || transaction->num_waiting) { + list_add(server.transactions, transaction); } else { // There are no other transactions in progress, and this one has nothing // to wait for, so we can skip the queue. @@ -359,12 +356,24 @@ static void set_instruction_ready( } } +/** + * Mark all of the view's instructions as ready up to and including the + * instruction at the given index. This allows the view to skip a configure. + */ +static void set_instructions_ready(struct sway_view *view, int index) { + for (int i = 0; i <= index; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + set_instruction_ready(instruction); + } +} + void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { for (int i = 0; i < view->swayc->instructions->length; ++i) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (instruction->serial == serial && !instruction->ready) { - set_instruction_ready(instruction); + set_instructions_ready(view, i); return; } } @@ -377,7 +386,7 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, view->swayc->instructions->items[i]; if (!instruction->ready && instruction->state.view_width == width && instruction->state.view_height == height) { - set_instruction_ready(instruction); + set_instructions_ready(view, i); return; } } diff --git a/sway/server.c b/sway/server.c index a2bc5702..884d971e 100644 --- a/sway/server.c +++ b/sway/server.c @@ -119,6 +119,8 @@ bool server_init(struct sway_server *server) { } server->destroying_containers = create_list(); + server->transactions = create_list(); + input_manager = input_manager_create(server); return true; } @@ -127,6 +129,7 @@ void server_fini(struct sway_server *server) { // TODO: free sway-specific resources wl_display_destroy(server->wl_display); list_free(server->destroying_containers); + list_free(server->transactions); } void server_run(struct sway_server *server) { From e8fb6b3325ee545312c092f0b103eea2424fce9f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 19:36:22 +1000 Subject: [PATCH 29/34] Fix crash when moving last child of a container to workspace or output We were arranging a parent which may have been deleted by the reaper, which meant the `current` children list of the surviving parent had a dangling pointer. Instead, we now reap the workspace. --- sway/commands/move.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 4ce8d089..4061df3a 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -90,6 +90,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, } free(ws_name); struct sway_container *old_parent = current->parent; + struct sway_container *old_ws = container_parent(current, C_WORKSPACE); struct sway_container *destination = seat_get_focus_inactive( config->handler_context.seat, ws); container_move_to(current, destination); @@ -99,8 +100,11 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, container_reap_empty(old_parent); container_reap_empty(destination->parent); + // TODO: Ideally we would arrange the surviving parent after reaping, + // but container_reap_empty does not return it, so we arrange the + // workspace instead. struct sway_transaction *txn = transaction_create(); - arrange_windows(old_parent, txn); + arrange_windows(old_ws, txn); arrange_windows(destination->parent, txn); transaction_commit(txn); @@ -129,13 +133,17 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, focus = destination->children->items[0]; } struct sway_container *old_parent = current->parent; + struct sway_container *old_ws = container_parent(current, C_WORKSPACE); container_move_to(current, focus); seat_set_focus(config->handler_context.seat, old_parent); container_reap_empty(old_parent); container_reap_empty(focus->parent); + // TODO: Ideally we would arrange the surviving parent after reaping, + // but container_reap_empty does not return it, so we arrange the + // workspace instead. struct sway_transaction *txn = transaction_create(); - arrange_windows(old_parent, txn); + arrange_windows(old_ws, txn); arrange_windows(focus->parent, txn); transaction_commit(txn); From d7169ee7ffd45318125dbe3013aadbd1482a3e5f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 19:44:54 +1000 Subject: [PATCH 30/34] Replace list_empty with a simple alternative --- common/list.c | 7 ------- include/list.h | 1 - sway/desktop/transaction.c | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/common/list.c b/common/list.c index cab10c1e..39cc10e1 100644 --- a/common/list.c +++ b/common/list.c @@ -62,13 +62,6 @@ void list_cat(list_t *list, list_t *source) { } } -void list_empty(list_t *list) { - list->capacity = 10; - list->length = 0; - free(list->items); - list->items = malloc(sizeof(void*) * list->capacity); -} - void list_qsort(list_t *list, int compare(const void *left, const void *right)) { qsort(list->items, list->length, sizeof(void *), compare); } diff --git a/include/list.h b/include/list.h index d352dbd5..7eead4ac 100644 --- a/include/list.h +++ b/include/list.h @@ -14,7 +14,6 @@ void list_add(list_t *list, void *item); void list_insert(list_t *list, int index, void *item); void list_del(list_t *list, int index); void list_cat(list_t *list, list_t *source); -void list_empty(list_t *list); // See qsort. Remember to use *_qsort functions as compare functions, // because they dereference the left and right arguments first! void list_qsort(list_t *list, int compare(const void *left, const void *right)); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7a2e78e5..fc23ef35 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -241,7 +241,7 @@ static void transaction_progress_queue() { transaction_apply(transaction); transaction_destroy(transaction); } - list_empty(server.transactions); + server.transactions->length = 0; } static int handle_timeout(void *data) { From 3c81a900b766dd2c049ba7af6e603805893e0926 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 19:52:31 +1000 Subject: [PATCH 31/34] Add comment about usage to arrange_windows declaration --- include/sway/tree/arrange.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index 6c8c0dba..58235642 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -16,6 +16,10 @@ void add_gaps(struct sway_container *c); * * Use this function if you need to arrange multiple sections of the tree in one * transaction. + * + * You must set the desired state of the container before calling + * arrange_windows, then don't change any state-tracked properties in the + * container until you've called transaction_commit. */ void arrange_windows(struct sway_container *container, struct sway_transaction *transaction); From 3a6ed5110c76ef5bed8cc4c26a97759f6201eaac Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 21:13:22 +1000 Subject: [PATCH 32/34] 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(-) 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; } From 96c8c024830f13a27790d4ea36b640df383a7f49 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 30 Jun 2018 14:30:14 +1000 Subject: [PATCH 33/34] Fix flash of background when xwayland views are mapped A flash of background was happening for two reasons: 1) We were using the xsurface's dimensions to check if the surface is ready, but these are pending dimensions. 2) In my particular setup, the default geometry of the xsurface does not intersect any output, which prevented it from receiving a frame done event. This made the transaction time out and the client would only redraw once it's been rendered. --- sway/desktop/transaction.c | 7 +++++++ sway/desktop/xwayland.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7c5a9b8f..d2932c87 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -298,6 +298,13 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); ++transaction->num_waiting; + + // From here on we are rendering a saved buffer of the view, which + // means we can send a frame done event to make the client redraw it + // as soon as possible. Additionally, this is required if a view is + // mapping and its default geometry doesn't intersect an output. + struct timespec when; + wlr_surface_send_frame_done(con->sway_view->surface, &when); } list_add(con->instructions, instruction); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 023fb2a7..ad893248 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -269,10 +269,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, commit); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + struct wlr_surface_state *surface_state = xsurface->surface->current; if (view->swayc->instructions->length) { transaction_notify_view_ready_by_size(view, - xsurface->width, xsurface->height); + surface_state->width, surface_state->height); } view_damage_from(view); } From fc6fde7d90ee031539252cb8832e11c38cfed686 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 30 Jun 2018 21:07:54 +1000 Subject: [PATCH 34/34] Fix compile error --- sway/commands/move.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 4061df3a..a4fae388 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -184,7 +184,7 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, } static struct cmd_results *move_in_direction(struct sway_container *container, - enum wlr_direction direction, int move_amt) { + enum movement_direction direction, int move_amt) { if (container->type == C_WORKSPACE) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move workspaces in a direction");