From 1fd62d3e0cb28aba9708ea941350a44f81a5418c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 16 Aug 2018 23:21:30 +0100 Subject: [PATCH 1/8] Fix swaylock zsh completions --- completions/zsh/_swaylock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/completions/zsh/_swaylock b/completions/zsh/_swaylock index 478c7512e..8fb4834c0 100644 --- a/completions/zsh/_swaylock +++ b/completions/zsh/_swaylock @@ -6,9 +6,9 @@ _arguments -s \ '(-v --version)'{-v,--version}'[Show the version number and quit]' \ '(-h --help)'{-h,--help}'[Show help message and quit]' \ - '(-f --daemonize)'{-f, --daemonize}'[Detach from the controlling terminal]'\ + '(-f --daemonize)'{-f,--daemonize}'[Detach from the controlling terminal]' \ '(-c --color)'{-c,--color}'[Specify a color (rrggbb)]' \ '(-i --image)'{-i,--image}'[Display an image]:files:_files' \ '(-s --scaling)'{-s,--scaling}'[Scaling mode]:mode:(stretch fill fit center tile)' \ - '(-u --no-unlock-indicator)'{-u,--no-unlock-indicator}'[Disable the unlock indicator]' - '(--socket)'{--socket}'[Use the specified socket path.]:files:_files' \ + '(-u --no-unlock-indicator)'{-u,--no-unlock-indicator}'[Disable the unlock indicator]' \ + '(--socket)'--socket'[Use the specified socket path.]:files:_files' \ From f0d21c46dd8eedc186509cf0ed20833b746182e7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 20 Aug 2018 15:06:12 -0400 Subject: [PATCH 2/8] Fix bad-free in swaynag --- include/swaynag/swaynag.h | 2 +- swaynag/config.c | 4 ++-- swaynag/main.c | 15 +++++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 1bf8b6405..a32d15030 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -58,7 +58,7 @@ struct swaynag_details { int offset; int visible_lines; int total_lines; - struct swaynag_button button_details; + struct swaynag_button *button_details; struct swaynag_button button_up; struct swaynag_button button_down; }; diff --git a/swaynag/config.c b/swaynag/config.c index d6c5739da..4d0824c98 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -180,8 +180,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, break; case 'L': // Detailed Button Text if (swaynag) { - free(swaynag->details.button_details.text); - swaynag->details.button_details.text = strdup(optarg); + free(swaynag->details.button_details->text); + swaynag->details.button_details->text = strdup(optarg); } break; case 'm': // Message diff --git a/swaynag/main.c b/swaynag/main.c index 6b0b52361..d1a0d236e 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -34,9 +34,10 @@ int main(int argc, char **argv) { button_close->type = SWAYNAG_ACTION_DISMISS; list_add(swaynag.buttons, button_close); - swaynag.details.button_details.text = strdup("Toggle Details"); - swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND; - + swaynag.details.button_details = + calloc(sizeof(struct swaynag_button), 1); + swaynag.details.button_details->text = strdup("Toggle Details"); + swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; char *config_path = NULL; bool debug = false; @@ -99,9 +100,10 @@ int main(int argc, char **argv) { swaynag_types_free(types); if (swaynag.details.message) { - list_add(swaynag.buttons, &swaynag.details.button_details); + list_add(swaynag.buttons, swaynag.details.button_details); } else { - free(swaynag.details.button_details.text); + free(swaynag.details.button_details->text); + free(swaynag.details.button_details); } wlr_log(WLR_DEBUG, "Output: %s", swaynag.type->output); @@ -123,7 +125,8 @@ int main(int argc, char **argv) { cleanup: swaynag_types_free(types); - free(swaynag.details.button_details.text); + free(swaynag.details.button_details->text); + free(swaynag.details.button_details); swaynag_destroy(&swaynag); return exit_code; } From 471533be0a1932b2444faca54493a0fcfe618922 Mon Sep 17 00:00:00 2001 From: minus Date: Mon, 20 Aug 2018 23:17:02 +0200 Subject: [PATCH 3/8] Improve new workspace name selection Improves upon 18e425ed by using the first assigned workspace instead of the last one. The order isn't explicitly guaranteed to be the same as in the config, but in general works. --- sway/tree/workspace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 292f2c9ae..cf50ee09e 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -205,6 +205,7 @@ char *workspace_next_name(const char *output_name) { && workspace_by_name(wso->workspace) == NULL) { free(target); target = strdup(wso->workspace); + break; } } if (target != NULL) { From f129b1b89fe0726f00a54f4c4857a7a8c5fab220 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 21 Aug 2018 12:41:57 +1000 Subject: [PATCH 4/8] Replace enum resize_edge with wlr_edges --- include/sway/tree/layout.h | 10 ++-------- sway/commands/resize.c | 9 +++++---- sway/input/seat.c | 2 +- sway/tree/layout.c | 6 +++--- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 5b803dfe6..519189d94 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -15,13 +15,7 @@ enum movement_direction { MOVE_CHILD, }; -enum resize_edge { - RESIZE_EDGE_NONE = 0, - RESIZE_EDGE_LEFT = 1, - RESIZE_EDGE_RIGHT = 2, - RESIZE_EDGE_TOP = 4, - RESIZE_EDGE_BOTTOM = 8, -}; +enum wlr_edges; struct sway_container; @@ -52,7 +46,7 @@ struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout); void container_recursive_resize(struct sway_container *container, - double amount, enum resize_edge edge); + double amount, enum wlr_edges edge); void container_swap(struct sway_container *con1, struct sway_container *con2); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 0f3005f4c..ea1e36ff6 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "sway/commands.h" #include "sway/tree/arrange.h" @@ -250,10 +251,10 @@ static void resize_tiled(struct sway_container *parent, int amount, } } - enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ? - RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP; - enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ? - RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM; + enum wlr_edges minor_edge = axis == RESIZE_AXIS_HORIZONTAL ? + WLR_EDGE_LEFT : WLR_EDGE_TOP; + enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ? + WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM; for (int i = 0; i < parent->parent->children->length; i++) { struct sway_container *sibling = parent->parent->children->items[i]; diff --git a/sway/input/seat.c b/sway/input/seat.c index caee37a6b..4077a8ddb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -984,7 +984,7 @@ void seat_begin_resize_floating(struct sway_seat *seat, seat->op_resize_preserve_ratio = keyboard && (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); seat->op_resize_edge = edge == WLR_EDGE_NONE ? - RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; + WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; seat->op_button = button; seat->op_ref_lx = seat->cursor->cursor->x; seat->op_ref_ly = seat->cursor->cursor->y; diff --git a/sway/tree/layout.c b/sway/tree/layout.c index ee7d7418d..a3de44ce1 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -876,13 +876,13 @@ struct sway_container *container_split(struct sway_container *child, } void container_recursive_resize(struct sway_container *container, - double amount, enum resize_edge edge) { + double amount, enum wlr_edges edge) { bool layout_match = true; wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount); - if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { + if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { container->width += amount; layout_match = container->layout == L_HORIZ; - } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { + } else if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { container->height += amount; layout_match = container->layout == L_VERT; } From e81c26fab910505719745f5eb7060457fd7ba7b0 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 22 Aug 2018 08:23:29 +1000 Subject: [PATCH 5/8] Fix sending fullscreen event to view Fixes #2504 --- sway/tree/container.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 6ea0cc946..a8c6e6677 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1201,6 +1201,7 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { container_set_fullscreen(workspace->sway_workspace->fullscreen, false); } + set_fullscreen_iterator(container, &enable); container_for_each_child(container, set_fullscreen_iterator, &enable); container->is_fullscreen = enable; From 03718aaebb519bb38282395023306d980d70d459 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Tue, 21 Aug 2018 20:51:01 -0400 Subject: [PATCH 6/8] Add app_id and class to get_tree output --- sway/ipc-json.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index f40af0433..06cb7e11c 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -5,6 +5,7 @@ #include "sway/config.h" #include "sway/ipc-json.h" #include "sway/tree/container.h" +#include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "sway/output.h" #include "sway/input/input-manager.h" @@ -192,6 +193,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object c->name ? json_object_new_string(c->name) : NULL); json_object_object_add(object, "type", json_object_new_string("con")); + if (c->type == C_VIEW) { + const char *app_id = view_get_app_id(c->sway_view); + json_object_object_add(object, "app_id", + app_id ? json_object_new_string(app_id) : NULL); + + const char *class = view_get_class(c->sway_view); + json_object_object_add(object, "class", + class ? json_object_new_string(class) : NULL); + } + if (c->parent) { json_object_object_add(object, "layout", json_object_new_string(ipc_json_layout_description(c->layout))); From e1d5dc08048d8b0ef739302ee58bdc8642b44f13 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 22 Aug 2018 17:46:12 +1000 Subject: [PATCH 7/8] Translate floating containers when a workspace is moved When a workspace is moved to another output, or the output it's on changes its global layout position, the floating containers on that workspace should be translated by the same amount as the workspace. This keeps the floating containers in the same position relative to the workspace. A check is done to make sure the floating container's center point isn't being moved off screen. If it is, it is centered within the workspace. Fixes part of #2500. --- sway/tree/arrange.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index cf4a5d9a7..a4b058f37 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -206,10 +206,30 @@ static void arrange_workspace(struct sway_container *workspace) { wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", area->width, area->height, area->x, area->y); remove_gaps(workspace); + + double prev_x = workspace->x; + double prev_y = workspace->y; workspace->width = area->width; workspace->height = area->height; workspace->x = output->x + area->x; workspace->y = output->y + area->y; + + // Adjust any floating containers + double diff_x = workspace->x - prev_x; + double diff_y = workspace->y - prev_y; + for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) { + struct sway_container *floater = + workspace->sway_workspace->floating->items[i]; + container_floating_translate(floater, diff_x, diff_y); + double center_x = floater->x + floater->width / 2; + double center_y = floater->y + floater->height / 2; + struct wlr_box workspace_box; + container_get_box(workspace, &workspace_box); + if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { + container_floating_move_to_center(floater); + } + } + add_gaps(workspace); container_set_dirty(workspace); wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, From b6058703fa240780d66fac8ef96982c66b2b0263 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 20 Aug 2018 15:54:30 +1000 Subject: [PATCH 8/8] Refactor destroy functions and save workspaces when there's no outputs This changes the destroy functions to the following: * output_begin_destroy * output_destroy * workspace_begin_destroy * workspace_destroy * container_begin_destroy * container_destroy * view_begin_destroy * view_destroy The terminology was `destroy` and `free`, and it has been changed to `begin_destroy` and `destroy` respectively. When the last output is disconnected, its workspaces will now be stashed in the root. Upon connection of a new output they will be restored. There is a new function `workspace_consider_destroy` which decides whether the given workspace should be destroyed or not (ie. empty and not visible). Calling container_begin_destroy will no longer automatically reap the parents. In some places we want to reap the parents and in some we don't, so this is left to the caller. container_reap_empty_recursive and container_reap_empty have been combined into one function and it will recurse up the tree. --- include/sway/output.h | 6 + include/sway/tree/container.h | 30 +--- include/sway/tree/root.h | 1 + include/sway/tree/view.h | 4 +- include/sway/tree/workspace.h | 9 ++ sway/commands/move.c | 2 +- sway/config/output.c | 4 +- sway/desktop/output.c | 4 +- sway/desktop/transaction.c | 17 +- sway/desktop/xdg_shell.c | 2 +- sway/desktop/xdg_shell_v6.c | 2 +- sway/desktop/xwayland.c | 2 +- sway/input/seat.c | 21 +-- sway/tree/container.c | 284 +++++----------------------------- sway/tree/layout.c | 4 +- sway/tree/output.c | 108 ++++++++++++- sway/tree/root.c | 2 + sway/tree/view.c | 10 +- sway/tree/workspace.c | 63 +++++++- 19 files changed, 267 insertions(+), 308 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index d0d034b3c..098540fb6 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -39,6 +39,12 @@ struct sway_output { } events; }; +struct sway_container *output_create(struct sway_output *sway_output); + +void output_destroy(struct sway_container *output); + +void output_begin_destroy(struct sway_container *output); + typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *box, float rotation, void *user_data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index cd886cd05..2cb23d3c8 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -176,27 +176,6 @@ struct sway_container *container_create(enum sway_container_type type); const char *container_type_to_str(enum sway_container_type type); -struct sway_container *output_create(struct sway_output *sway_output); - -/** - * Create a new container container. A container container can be a a child of - * a workspace container or another container container. - */ -struct sway_container *container_container_create(); - -/** - * Create a new output. Outputs are children of the root container and have no - * order in the tree structure. - */ -struct sway_container *output_create(struct sway_output *sway_output); - -/** - * Create a new workspace container. Workspaces are children of an output - * container and are ordered alphabetically by name. - */ -struct sway_container *workspace_create(struct sway_container *output, - const char *name); - /* * Create a new view container. A view can be a child of a workspace container * or a container container and are rendered in the order and structure of @@ -205,9 +184,9 @@ 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); +void container_destroy(struct sway_container *con); -struct sway_container *container_destroy(struct sway_container *container); +void container_begin_destroy(struct sway_container *con); struct sway_container *container_close(struct sway_container *container); @@ -255,10 +234,7 @@ void container_update_textures_recursive(struct sway_container *con); void container_damage_whole(struct sway_container *container); -bool container_reap_empty(struct sway_container *con); - -struct sway_container *container_reap_empty_recursive( - struct sway_container *con); +struct sway_container *container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index d1f04a964..ee4bd836f 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -24,6 +24,7 @@ struct sway_root { struct wl_list outputs; // sway_output::link list_t *scratchpad; // struct sway_container + list_t *saved_workspaces; // For when there's no connected outputs struct { struct wl_signal new_container; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5fdecc2b0..f73ce571a 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -284,10 +284,10 @@ void view_for_each_popup(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_begin_destroy(struct sway_view *view); + void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); void view_unmap(struct sway_view *view); diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 35c91017c..efcb7c699 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -18,6 +18,15 @@ extern char *prev_workspace_name; struct sway_container *workspace_get_initial_output(const char *name); +struct sway_container *workspace_create(struct sway_container *output, + const char *name); + +void workspace_destroy(struct sway_container *workspace); + +void workspace_begin_destroy(struct sway_container *workspace); + +void workspace_consider_destroy(struct sway_container *ws); + char *workspace_next_name(const char *output_name); bool workspace_switch(struct sway_container *workspace, diff --git a/sway/commands/move.c b/sway/commands/move.c index c6dc07759..4c0189ec7 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -247,7 +247,7 @@ static void workspace_move_to_output(struct sway_container *workspace, } // Try to remove an empty workspace from the destination output. - container_reap_empty_recursive(new_output_focus); + container_reap_empty(new_output_focus); output_sort_workspaces(output); seat_set_focus(seat, output); diff --git a/sway/config/output.c b/sway/config/output.c index 1d8cb3efa..7f9b1007a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -181,13 +181,11 @@ void apply_output_config(struct output_config *oc, struct sway_container *output struct wlr_output *wlr_output = output->sway_output->wlr_output; if (oc && oc->enabled == 0) { - struct sway_output *sway_output = output->sway_output; if (output->sway_output->bg_pid != 0) { terminate_swaybg(output->sway_output->bg_pid); output->sway_output->bg_pid = 0; } - container_destroy(output); - sway_output->swayc = NULL; + output_begin_destroy(output); wlr_output_layout_remove(root_container.sway_root->output_layout, wlr_output); return; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3d8bbff56..401d3c44f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -498,7 +498,7 @@ void output_damage_whole_container(struct sway_output *output, static void damage_handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, damage_destroy); - container_destroy(output->swayc); + output_begin_destroy(output->swayc); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -506,7 +506,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_signal_emit(&output->events.destroy, output); if (output->swayc) { - container_destroy(output->swayc); + output_begin_destroy(output->swayc); } wl_list_remove(&output->link); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index f82e5ef2e..c18529fb0 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -54,7 +54,22 @@ static void transaction_destroy(struct sway_transaction *transaction) { con->instruction = NULL; } if (con->destroying && con->ntxnrefs == 0) { - container_free(con); + switch (con->type) { + case C_ROOT: + break; + case C_OUTPUT: + output_destroy(con); + break; + case C_WORKSPACE: + workspace_destroy(con); + break; + case C_CONTAINER: + case C_VIEW: + container_destroy(con); + break; + case C_TYPES: + break; + } } free(instruction); } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index aae129bd3..f5aaa5754 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -448,7 +448,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); view->wlr_xdg_surface = NULL; - view_destroy(view); + view_begin_destroy(view); } struct sway_view *view_from_wlr_xdg_surface( diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 277c53a3d..f623b77be 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -441,7 +441,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->map.link); wl_list_remove(&xdg_shell_v6_view->unmap.link); view->wlr_xdg_surface_v6 = NULL; - view_destroy(view); + view_begin_destroy(view); } struct sway_view *view_from_wlr_xdg_surface_v6( diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index ce7235e49..6fcc850db 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -341,7 +341,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->map.link); wl_list_remove(&xwayland_view->unmap.link); - view_destroy(&xwayland_view->view); + view_begin_destroy(&xwayland_view->view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 4077a8ddb..997d78151 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -694,13 +694,11 @@ void seat_set_focus_warp(struct sway_seat *seat, // clean up unfocused empty workspace on new output if (new_output_last_ws) { - if (!workspace_is_visible(new_output_last_ws) - && workspace_is_empty(new_output_last_ws)) { - if (last_workspace == new_output_last_ws) { - last_focus = NULL; - last_workspace = NULL; - } - container_destroy(new_output_last_ws); + workspace_consider_destroy(new_output_last_ws); + if (new_output_last_ws->destroying && + last_workspace == new_output_last_ws) { + last_focus = NULL; + last_workspace = NULL; } } @@ -716,12 +714,9 @@ void seat_set_focus_warp(struct sway_seat *seat, if (notify && last_workspace != new_workspace) { ipc_event_workspace(last_workspace, new_workspace, "focus"); } - if (!workspace_is_visible(last_workspace) - && workspace_is_empty(last_workspace)) { - if (last_workspace == last_focus) { - last_focus = NULL; - } - container_destroy(last_workspace); + workspace_consider_destroy(last_workspace); + if (last_workspace->destroying && last_workspace == last_focus) { + last_focus = NULL; } } diff --git a/sway/tree/container.c b/sway/tree/container.c index a8c6e6677..6da6212c9 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -104,209 +104,52 @@ struct sway_container *container_create(enum sway_container_type type) { return c; } -static void container_workspace_free(struct sway_workspace *ws) { - list_foreach(ws->output_priority, free); - list_free(ws->output_priority); - list_free(ws->floating); - free(ws); -} - -void container_free(struct sway_container *cont) { - if (!sway_assert(cont->destroying, +void container_destroy(struct sway_container *con) { + if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, + "Expected a container or view")) { + return; + } + if (!sway_assert(con->destroying, "Tried to free container which wasn't marked as destroying")) { return; } - if (!sway_assert(cont->ntxnrefs == 0, "Tried to free container " + if (!sway_assert(con->ntxnrefs == 0, "Tried to free container " "which is still referenced by transactions")) { return; } - free(cont->name); - free(cont->formatted_title); - 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); - list_free(cont->children); - list_free(cont->current.children); - list_free(cont->outputs); + free(con->name); + free(con->formatted_title); + wlr_texture_destroy(con->title_focused); + wlr_texture_destroy(con->title_focused_inactive); + wlr_texture_destroy(con->title_unfocused); + wlr_texture_destroy(con->title_urgent); + list_free(con->children); + list_free(con->current.children); + list_free(con->outputs); - switch (cont->type) { - case C_ROOT: - break; - case C_OUTPUT: - 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 (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->swayc = NULL; + free(view->title_format); + view->title_format = NULL; - if (view->destroying) { - view_free(view); - } + if (view->destroying) { + view_destroy(view); } - break; - case C_TYPES: - sway_assert(false, "Didn't expect to see C_TYPES here"); - break; } - free(cont); + free(con); } -static struct sway_container *container_destroy_noreaping( - struct sway_container *con); - -static struct sway_container *container_workspace_destroy( - struct sway_container *workspace) { - if (!sway_assert(workspace, "cannot destroy null workspace")) { - return NULL; - } - - struct sway_container *output = container_parent(workspace, C_OUTPUT); - - // If we're destroying the output, it will be NULL here. Return the root so - // that it doesn't appear that the workspace has refused to be destoyed, - // which would leave it in a broken state with no parent. - if (output == NULL) { - return &root_container; - } - - // Do not destroy this if it's the last workspace on this output - if (output->children->length == 1) { - return NULL; - } - - wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name); - - if (!workspace_is_empty(workspace)) { - // Move children to a different workspace on this output - struct sway_container *new_workspace = NULL; - for (int i = 0; i < output->children->length; i++) { - if (output->children->items[i] != workspace) { - new_workspace = output->children->items[i]; - break; - } - } - - wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'", - workspace->name, new_workspace->name); - for (int i = 0; i < workspace->children->length; i++) { - container_move_to(workspace->children->items[i], new_workspace); - } - list_t *floating = workspace->sway_workspace->floating; - for (int i = 0; i < floating->length; i++) { - struct sway_container *floater = floating->items[i]; - container_remove_child(floater); - workspace_add_floating(new_workspace, floater); - } - } - - return output; -} - -static void untrack_output(struct sway_container *con, void *data) { - struct sway_output *output = data; - int index = list_find(con->outputs, output); - if (index != -1) { - list_del(con->outputs, index); - } -} - -static struct sway_container *container_output_destroy( - struct sway_container *output) { - if (!sway_assert(output, "cannot destroy null output")) { - return NULL; - } - - if (output->children->length > 0) { - // TODO save workspaces when there are no outputs. - // TODO also check if there will ever be no outputs except for exiting - // program - if (root_container.children->length > 1) { - // Move workspace from this output to another output - struct sway_container *fallback_output = - root_container.children->items[0]; - if (fallback_output == output) { - fallback_output = root_container.children->items[1]; - } - - while (output->children->length) { - struct sway_container *workspace = output->children->items[0]; - - struct sway_container *new_output = - workspace_output_get_highest_available(workspace, output); - if (!new_output) { - new_output = fallback_output; - workspace_output_add_priority(workspace, new_output); - } - - container_remove_child(workspace); - if (!workspace_is_empty(workspace)) { - container_add_child(new_output, workspace); - ipc_event_workspace(NULL, workspace, "move"); - } else { - container_destroy(workspace); - } - - output_sort_workspaces(new_output); - } - } - } - - root_for_each_container(untrack_output, output->sway_output); - - wl_list_remove(&output->sway_output->mode.link); - wl_list_remove(&output->sway_output->transform.link); - wl_list_remove(&output->sway_output->scale.link); - - wl_list_remove(&output->sway_output->damage_destroy.link); - wl_list_remove(&output->sway_output->damage_frame.link); - - output->sway_output->swayc = NULL; - output->sway_output = NULL; - - wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); - - return &root_container; -} - -/** - * Implement the actual destroy logic, without reaping. - */ -static struct sway_container *container_destroy_noreaping( - struct sway_container *con) { - if (con == NULL) { - return NULL; - } - if (con->destroying) { - return NULL; +void container_begin_destroy(struct sway_container *con) { + if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, + "Expected a container or view")) { + return; } wl_signal_emit(&con->events.destroy, con); - - // emit IPC event if (con->type == C_VIEW) { ipc_event_window(con, "close"); - } else if (con->type == C_WORKSPACE) { - ipc_event_workspace(NULL, con, "empty"); - } - - // 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; - } } container_end_mouse_operation(con); @@ -318,51 +161,22 @@ static struct sway_container *container_destroy_noreaping( root_scratchpad_remove_container(con); } - if (!con->parent) { - return NULL; + if (con->parent) { + container_remove_child(con); } - - return container_remove_child(con); } -bool container_reap_empty(struct sway_container *con) { - switch (con->type) { - case C_ROOT: - case C_OUTPUT: - // dont reap these - break; - case C_WORKSPACE: - if (!workspace_is_visible(con) && workspace_is_empty(con)) { - wlr_log(WLR_DEBUG, "Destroying workspace via reaper"); - container_destroy_noreaping(con); - return true; - } - break; - case C_CONTAINER: - if (con->children->length == 0) { - container_destroy_noreaping(con); - return true; - } - case C_VIEW: - break; - case C_TYPES: - sway_assert(false, "container_reap_empty called on an invalid " - "container"); - break; - } - - return false; -} - -struct sway_container *container_reap_empty_recursive( - struct sway_container *con) { - while (con) { +struct sway_container *container_reap_empty(struct sway_container *con) { + while (con && con->type == C_CONTAINER) { struct sway_container *next = con->parent; - if (!container_reap_empty(con)) { - break; + if (con->children->length == 0) { + container_begin_destroy(con); } con = next; } + if (con && con->type == C_WORKSPACE) { + workspace_consider_destroy(con); + } return con; } @@ -371,34 +185,12 @@ 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_noreaping(container); + container_begin_destroy(container); container = parent; } 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(). - * - * This function just wraps container_destroy_noreaping(), then does reaping. - */ -struct sway_container *container_destroy(struct sway_container *con) { - if (con->is_fullscreen) { - struct sway_container *ws = container_parent(con, C_WORKSPACE); - ws->sway_workspace->fullscreen = NULL; - } - struct sway_container *parent = container_destroy_noreaping(con); - - if (!parent) { - return NULL; - } - - return container_reap_empty_recursive(parent); -} - static void container_close_func(struct sway_container *container, void *data) { if (container->type == C_VIEW) { view_close(container->sway_view); diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a3de44ce1..12e7342bb 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -302,7 +302,7 @@ static void workspace_rejigger(struct sway_container *ws, move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; container_flatten(ws); - container_reap_empty_recursive(original_parent); + container_reap_empty(original_parent); container_create_notify(new_parent); } @@ -325,7 +325,7 @@ static void move_out_of_tabs_stacks(struct sway_container *container, container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1); } else { container_insert_child(new_parent, container, offs < 0 ? 0 : 1); - container_reap_empty_recursive(new_parent->parent); + container_reap_empty(new_parent->parent); container_flatten(new_parent->parent); } container_create_notify(new_parent); diff --git a/sway/tree/output.c b/sway/tree/output.c index 6da630643..80636c113 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -10,6 +10,7 @@ #include "log.h" static void restore_workspaces(struct sway_container *output) { + // Workspace output priority for (int i = 0; i < root_container.children->length; i++) { struct sway_container *other = root_container.children->items[i]; if (other == output) { @@ -29,6 +30,15 @@ static void restore_workspaces(struct sway_container *output) { } } + // Saved workspaces + list_t *saved = root_container.sway_root->saved_workspaces; + for (int i = 0; i < saved->length; ++i) { + struct sway_container *ws = saved->items[i]; + container_add_child(output, ws); + ipc_event_workspace(NULL, ws, "move"); + } + saved->length = 0; + output_sort_workspaces(output); } @@ -68,7 +78,7 @@ struct sway_container *output_create( output->sway_output = sway_output; output->name = strdup(name); if (output->name == NULL) { - container_destroy(output); + output_begin_destroy(output); return NULL; } @@ -103,6 +113,102 @@ struct sway_container *output_create( return output; } +static void output_evacuate(struct sway_container *output) { + if (!output->children->length) { + return; + } + struct sway_container *fallback_output = NULL; + if (root_container.children->length > 1) { + fallback_output = root_container.children->items[0]; + if (fallback_output == output) { + fallback_output = root_container.children->items[1]; + } + } + + while (output->children->length) { + struct sway_container *workspace = output->children->items[0]; + + struct sway_container *new_output = + workspace_output_get_highest_available(workspace, output); + if (!new_output) { + new_output = fallback_output; + } + + container_remove_child(workspace); + + if (new_output) { + workspace_output_add_priority(workspace, new_output); + container_add_child(new_output, workspace); + output_sort_workspaces(new_output); + ipc_event_workspace(NULL, workspace, "move"); + } else { + list_add(root_container.sway_root->saved_workspaces, workspace); + } + } +} + +void output_destroy(struct sway_container *output) { + if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { + return; + } + if (!sway_assert(output->destroying, + "Tried to free output which wasn't marked as destroying")) { + return; + } + if (!sway_assert(output->ntxnrefs == 0, "Tried to free output " + "which is still referenced by transactions")) { + return; + } + free(output->name); + free(output->formatted_title); + wlr_texture_destroy(output->title_focused); + wlr_texture_destroy(output->title_focused_inactive); + wlr_texture_destroy(output->title_unfocused); + wlr_texture_destroy(output->title_urgent); + list_free(output->children); + list_free(output->current.children); + list_free(output->outputs); + free(output); + + // NOTE: We don't actually destroy the sway_output here +} + +static void untrack_output(struct sway_container *con, void *data) { + struct sway_output *output = data; + int index = list_find(con->outputs, output); + if (index != -1) { + list_del(con->outputs, index); + } +} + +void output_begin_destroy(struct sway_container *output) { + if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { + return; + } + wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); + wl_signal_emit(&output->events.destroy, output); + + output_evacuate(output); + + output->destroying = true; + container_set_dirty(output); + + root_for_each_container(untrack_output, output->sway_output); + + wl_list_remove(&output->sway_output->mode.link); + wl_list_remove(&output->sway_output->transform.link); + wl_list_remove(&output->sway_output->scale.link); + wl_list_remove(&output->sway_output->damage_destroy.link); + wl_list_remove(&output->sway_output->damage_frame.link); + + output->sway_output->swayc = NULL; + output->sway_output = NULL; + + if (output->parent) { + container_remove_child(output); + } +} + void output_for_each_workspace(struct sway_container *output, void (*f)(struct sway_container *con, void *data), void *data) { if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { diff --git a/sway/tree/root.c b/sway/tree/root.c index c27ff2c33..5602f0a03 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -39,6 +39,7 @@ void root_create(void) { wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); root_container.sway_root->scratchpad = create_list(); + root_container.sway_root->saved_workspaces = create_list(); root_container.sway_root->output_layout_change.notify = output_layout_handle_change; @@ -50,6 +51,7 @@ void root_destroy(void) { // sway_root wl_list_remove(&root_container.sway_root->output_layout_change.link); list_free(root_container.sway_root->scratchpad); + list_free(root_container.sway_root->saved_workspaces); wlr_output_layout_destroy(root_container.sway_root->output_layout); free(root_container.sway_root); diff --git a/sway/tree/view.c b/sway/tree/view.c index 7bf7325a6..ba4a880f7 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -35,7 +35,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, wl_signal_init(&view->events.unmap); } -void view_free(struct sway_view *view) { +void view_destroy(struct sway_view *view) { if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { return; } @@ -75,14 +75,14 @@ void view_free(struct sway_view *view) { * destroying flag will make the view get freed when the transaction is * finished. */ -void view_destroy(struct sway_view *view) { +void view_begin_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); + view_destroy(view); } } @@ -560,7 +560,9 @@ void view_unmap(struct sway_view *view) { } bool was_fullscreen = view->swayc->is_fullscreen; - struct sway_container *surviving_ancestor = container_destroy(view->swayc); + struct sway_container *parent = view->swayc->parent; + container_begin_destroy(view->swayc); + struct sway_container *surviving_ancestor = container_reap_empty(parent); // If the workspace wasn't reaped if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index cf50ee09e..93c4b3d31 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -79,6 +79,65 @@ struct sway_container *workspace_create(struct sway_container *output, return workspace; } +void workspace_destroy(struct sway_container *workspace) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return; + } + if (!sway_assert(workspace->destroying, + "Tried to free workspace which wasn't marked as destroying")) { + return; + } + if (!sway_assert(workspace->ntxnrefs == 0, "Tried to free workspace " + "which is still referenced by transactions")) { + return; + } + // sway_workspace + struct sway_workspace *ws = workspace->sway_workspace; + list_foreach(ws->output_priority, free); + list_free(ws->output_priority); + list_free(ws->floating); + free(ws); + + // swayc + free(workspace->name); + free(workspace->formatted_title); + wlr_texture_destroy(workspace->title_focused); + wlr_texture_destroy(workspace->title_focused_inactive); + wlr_texture_destroy(workspace->title_unfocused); + wlr_texture_destroy(workspace->title_urgent); + list_free(workspace->children); + list_free(workspace->current.children); + list_free(workspace->outputs); + free(workspace); +} + +void workspace_begin_destroy(struct sway_container *workspace) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return; + } + wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); + wl_signal_emit(&workspace->events.destroy, workspace); + ipc_event_workspace(NULL, workspace, "empty"); // intentional + + workspace->destroying = true; + container_set_dirty(workspace); + + if (workspace->parent) { + container_remove_child(workspace); + } +} + +void workspace_consider_destroy(struct sway_container *ws) { + if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0 + && seat_get_active_child(seat, ws->parent) != ws) { + workspace_begin_destroy(ws); + } +} + char *prev_workspace_name = NULL; void next_name_map(struct sway_container *ws, void *data) { @@ -421,9 +480,7 @@ bool workspace_switch(struct sway_container *workspace, // no op. We therefore need to send the IPC event and clean up the old // workspace here. ipc_event_workspace(active_ws, workspace, "focus"); - if (!workspace_is_visible(active_ws) && workspace_is_empty(active_ws)) { - container_destroy(active_ws); - } + workspace_consider_destroy(active_ws); } seat_set_focus(seat, next); struct sway_container *output = container_parent(workspace, C_OUTPUT);