diff options
Diffstat (limited to 'sway')
43 files changed, 953 insertions, 603 deletions
diff --git a/sway/commands.c b/sway/commands.c index 364c26da..d9c54adc 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -146,6 +146,7 @@ static struct cmd_handler command_handlers[] = { { "layout", cmd_layout }, { "mark", cmd_mark }, { "move", cmd_move }, + { "nop", cmd_nop }, { "opacity", cmd_opacity }, { "reload", cmd_reload }, { "rename", cmd_rename }, diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 0bc0929a..04582e88 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -22,27 +22,38 @@ struct cmd_results *cmd_assign(int argc, char **argv) { return error; } - ++argv; - int target_len = argc - 1; + --argc; ++argv; if (strncmp(*argv, "→", strlen("→")) == 0) { - if (argc < 3) { + if (argc < 2) { free(criteria); return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); } + --argc; ++argv; - --target_len; } if (strcmp(*argv, "output") == 0) { criteria->type = CT_ASSIGN_OUTPUT; - ++argv; - --target_len; + --argc; ++argv; } else { - criteria->type = CT_ASSIGN_WORKSPACE; + if (strcmp(*argv, "workspace") == 0) { + --argc; ++argv; + } + if (strcmp(*argv, "number") == 0) { + --argc; ++argv; + if (argv[0][0] < '0' || argv[0][0] > '9') { + free(criteria); + return cmd_results_new(CMD_INVALID, "assign", + "Invalid workspace number '%s'", argv[0]); + } + criteria->type = CT_ASSIGN_WORKSPACE_NUMBER; + } else { + criteria->type = CT_ASSIGN_WORKSPACE; + } } - criteria->target = join_args(argv, target_len); + criteria->target = join_args(argv, argc); list_add(config->criteria, criteria); wlr_log(WLR_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 00e39ae7..5ce7919b 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -26,7 +26,16 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { return error; } - tmp = join_args(argv + 1, argc - 1); + --argc; ++argv; + } + + if (argv[0][0] == '\'' || argv[0][0] == '"') { + if (argc > 0) { + return cmd_results_new(CMD_INVALID, "exec_always", + "command cannot be partially quoted"); + } + tmp = strdup(argv[0]); + strip_quotes(tmp); } else { tmp = join_args(argv, argc); } diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 31de5ec3..beafd9fb 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -8,6 +8,7 @@ #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "list.h" struct cmd_results *cmd_floating(int argc, char **argv) { @@ -24,7 +25,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { if (container->type == C_WORKSPACE) { // Wrap the workspace's children in a container so we can float it struct sway_container *workspace = container; - container = container_wrap_children(container); + container = workspace_wrap_children(container); workspace->layout = L_HORIZ; seat_set_focus(config->handler_context.seat, container); } @@ -32,7 +33,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(container)) { - while (container->parent->layout != L_FLOATING) { + while (container->parent->type != C_WORKSPACE) { container = container->parent; } } diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 76d3f1dc..6659a683 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -39,21 +39,24 @@ static struct cmd_results *focus_mode(struct sway_container *con, // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { - while (con->parent->layout != L_FLOATING) { + while (con->parent->type != C_WORKSPACE) { con = con->parent; } } struct sway_container *new_focus = NULL; if (floating) { - new_focus = seat_get_focus_inactive(seat, ws->sway_workspace->floating); + new_focus = seat_get_focus_inactive_floating(seat, ws); } else { new_focus = seat_get_focus_inactive_tiling(seat, ws); } - if (!new_focus) { - new_focus = ws; + if (new_focus) { + seat_set_focus(seat, new_focus); + } else { + return cmd_results_new(CMD_FAILURE, "focus", + "Failed to find a %s container in workspace", + floating ? "floating" : "tiling"); } - seat_set_focus(seat, new_focus); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 5ad06e40..a0661200 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -4,6 +4,7 @@ #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "sway/tree/layout.h" #include "util.h" @@ -21,7 +22,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { if (container->type == C_WORKSPACE) { // Wrap the workspace's children in a container so we can fullscreen it struct sway_container *workspace = container; - container = container_wrap_children(container); + container = workspace_wrap_children(container); workspace->layout = L_HORIZ; seat_set_focus(config->handler_context.seat, container); } diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index bb390f5f..d59c9fdb 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -31,7 +31,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { "<none|vertical|horizontal|both|smart>'"); } - container_for_each_descendant(&root_container, _configure_view, NULL); + root_for_each_container(_configure_view, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index de6b1b0a..c6dc0775 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include <ctype.h> #include <stdbool.h> #include <string.h> #include <strings.h> @@ -22,7 +23,7 @@ static const char *expected_syntax = "Expected 'move <left|right|up|down> <[px] px>' or " "'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or " - "'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or " + "'move <container|window|workspace> [to] output <name|direction>' or " "'move <container|window> [to] mark <mark>'"; static struct sway_container *output_in_direction(const char *direction, @@ -64,7 +65,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, return cmd_results_new(CMD_FAILURE, "move", "Can't move an empty workspace"); } - current = container_wrap_children(current); + current = workspace_wrap_children(current); } else if (current->type != C_CONTAINER && current->type != C_VIEW) { return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); @@ -124,7 +125,11 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, return cmd_results_new(CMD_INVALID, "move", expected_syntax); } - ws_name = strdup(argv[3]); + if (!isdigit(argv[3][0])) { + return cmd_results_new(CMD_INVALID, "move", + "Invalid workspace number '%s'", argv[3]); + } + ws_name = join_args(argv + 3, argc - 3); ws = workspace_by_number(ws_name); } else { ws_name = join_args(argv + 2, argc - 2); @@ -231,7 +236,6 @@ static void workspace_move_to_output(struct sway_container *workspace, seat_get_focus_inactive(seat, output); container_add_child(output, workspace); - wl_signal_emit(&workspace->events.reparent, old_output); // If moving the last workspace from the old output, create a new workspace // on the old output @@ -245,7 +249,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_sort_workspaces(output); + output_sort_workspaces(output); seat_set_focus(seat, output); workspace_output_raise_priority(workspace, old_output, output); ipc_event_workspace(NULL, workspace, "move"); @@ -437,14 +441,14 @@ static struct cmd_results *move_to_scratchpad(struct sway_container *con) { if (con->type == C_WORKSPACE) { // Wrap the workspace's children in a container struct sway_container *workspace = con; - con = container_wrap_children(con); + con = workspace_wrap_children(con); workspace->layout = L_HORIZ; } // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { - while (con->parent->layout != L_FLOATING) { + while (con->parent->type != C_WORKSPACE) { con = con->parent; } } diff --git a/sway/commands/nop.c b/sway/commands/nop.c new file mode 100644 index 00000000..c12fe15a --- /dev/null +++ b/sway/commands/nop.c @@ -0,0 +1,5 @@ +#include "sway/commands.h" + +struct cmd_results *cmd_nop(int argc, char **argv) { + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/rename.c b/sway/commands/rename.c index c6952bbb..21d2aa64 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include <ctype.h> #include <string.h> #include <strings.h> #include "log.h" @@ -6,6 +7,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/ipc-server.h" +#include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" @@ -33,6 +35,10 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } } else if (strcasecmp(argv[1], "number") == 0) { // 'rename workspace number x to new_name' + if (!isdigit(argv[2][0])) { + return cmd_results_new(CMD_INVALID, "rename", + "Invalid workspace number '%s'", argv[2]); + } workspace = workspace_by_number(argv[2]); while (argn < argc && strcasecmp(argv[argn], "to") != 0) { ++argn; @@ -66,7 +72,8 @@ struct cmd_results *cmd_rename(int argc, char **argv) { strcasecmp(new_name, "next_on_output") == 0 || strcasecmp(new_name, "prev_on_output") == 0 || strcasecmp(new_name, "back_and_forth") == 0 || - strcasecmp(new_name, "current") == 0) { + strcasecmp(new_name, "current") == 0 || + strcasecmp(new_name, "number") == 0) { free(new_name); return cmd_results_new(CMD_INVALID, "rename", "Cannot use special workspace name '%s'", argv[argn]); @@ -82,7 +89,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { free(workspace->name); workspace->name = new_name; - container_sort_workspaces(workspace->parent); + output_sort_workspaces(workspace->parent); ipc_event_workspace(NULL, workspace, "rename"); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 0f3005f4..ea1e36ff 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -5,6 +5,7 @@ #include <stdlib.h> #include <string.h> #include <strings.h> +#include <wlr/util/edges.h> #include <wlr/util/log.h> #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/commands/scratchpad.c b/sway/commands/scratchpad.c index 0e573aeb..7da20015 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -16,7 +16,7 @@ static void scratchpad_toggle_auto(void) { // If the focus is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(focus)) { - while (focus->parent->layout != L_FLOATING) { + while (focus->parent->type != C_WORKSPACE) { focus = focus->parent; } } @@ -33,9 +33,8 @@ static void scratchpad_toggle_auto(void) { // Check if there is an unfocused scratchpad window on the current workspace // and focus it. - for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { - struct sway_container *floater = - ws->sway_workspace->floating->children->items[i]; + for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { + struct sway_container *floater = ws->sway_workspace->floating->items[i]; if (floater->scratchpad && focus != floater) { wlr_log(WLR_DEBUG, "Focusing other scratchpad window (%s) in this workspace", @@ -103,7 +102,7 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { - while (con->parent->layout != L_FLOATING) { + while (con->parent->type != C_WORKSPACE) { con = con->parent; } } diff --git a/sway/commands/set.c b/sway/commands/set.c index ea388d3b..be51230b 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -25,23 +25,13 @@ void free_sway_variable(struct sway_variable *var) { } struct cmd_results *cmd_set(int argc, char **argv) { - char *tmp; struct cmd_results *error = NULL; if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) { return error; } if (argv[0][0] != '$') { - wlr_log(WLR_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); - - size_t size = snprintf(NULL, 0, "$%s", argv[0]); - tmp = malloc(size + 1); - if (!tmp) { - return cmd_results_new(CMD_FAILURE, "set", "Not possible to create variable $'%s'", argv[0]); - } - snprintf(tmp, size+1, "$%s", argv[0]); - - argv[0] = tmp; + return cmd_results_new(CMD_INVALID, "set", "variable '%s' must start with $", argv[0]); } struct sway_variable *var = NULL; diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index cf153a0a..dd7d170c 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -24,8 +24,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { - container_for_each_descendant(&root_container, - rebuild_marks_iterator, NULL); + root_for_each_container(rebuild_marks_iterator, NULL); } for (int i = 0; i < root_container.children->length; ++i) { diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 732ccb98..a0dd7215 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -36,5 +36,21 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { container->is_sticky = wants_sticky; + if (wants_sticky) { + // move container to focused workspace + struct sway_container *output = container_parent(container, C_OUTPUT); + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus_inactive(seat, output); + struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); + struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); + if (current_workspace != focused_workspace) { + container_move_to(container, focused_workspace); + arrange_windows(focused_workspace); + if (!container_reap_empty(current_workspace)) { + arrange_windows(current_workspace); + } + } + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 4e3a9cce..f881a002 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -50,13 +50,13 @@ struct cmd_results *cmd_swap(int argc, char **argv) { if (strcasecmp(argv[2], "id") == 0) { #ifdef HAVE_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); - other = container_find(&root_container, test_id, (void *)&id); + other = root_find_container(test_id, (void *)&id); #endif } else if (strcasecmp(argv[2], "con_id") == 0) { size_t con_id = atoi(value); - other = container_find(&root_container, test_con_id, (void *)con_id); + other = root_find_container(test_con_id, (void *)con_id); } else if (strcasecmp(argv[2], "mark") == 0) { - other = container_find(&root_container, test_mark, (void *)value); + other = root_find_container(test_mark, (void *)value); } else { free(value); return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); @@ -72,7 +72,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { || container_has_ancestor(other, current)) { error = cmd_results_new(CMD_FAILURE, "swap", "Cannot swap ancestor and descendant"); - } else if (current->layout == L_FLOATING || other->layout == L_FLOATING) { + } else if (container_is_floating(current) || container_is_floating(other)) { error = cmd_results_new(CMD_FAILURE, "swap", "Swapping with floating containers is not supported"); } diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 44ceccee..c183785b 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c @@ -52,8 +52,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { view_find_and_unmark(mark); } else { // Remove all marks from all views - container_for_each_descendant(&root_container, - remove_all_marks_iterator, NULL); + root_for_each_container(remove_all_marks_iterator, NULL); } free(mark); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index f5558bb4..ceb4cd6e 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include <ctype.h> #include <string.h> #include <strings.h> #include "sway/commands.h" @@ -60,9 +61,13 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { struct sway_container *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { - cmd_results_new(CMD_INVALID, "workspace", + return cmd_results_new(CMD_INVALID, "workspace", "Expected workspace number"); } + if (!isdigit(argv[1][0])) { + return cmd_results_new(CMD_INVALID, "workspace", + "Invalid workspace number '%s'", argv[1]); + } if (!(ws = workspace_by_number(argv[1]))) { char *name = join_args(argv + 1, argc - 1); ws = workspace_create(NULL, name); diff --git a/sway/config.c b/sway/config.c index bd14222a..642abbac 100644 --- a/sway/config.c +++ b/sway/config.c @@ -822,18 +822,7 @@ void config_update_font_height(bool recalculate) { size_t prev_max_height = config->font_height; config->font_height = 0; - container_for_each_descendant(&root_container, - find_font_height_iterator, &recalculate); - - // Also consider floating views - 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 *ws = output->children->items[j]; - container_for_each_descendant(ws->sway_workspace->floating, - find_font_height_iterator, &recalculate); - } - } + root_for_each_container(find_font_height_iterator, &recalculate); if (config->font_height != prev_max_height) { arrange_windows(&root_container); diff --git a/sway/criteria.c b/sway/criteria.c index a5df1eef..81c2325a 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -167,8 +167,7 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } list_t *urgent_views = create_list(); - container_for_each_descendant(&root_container, - find_urgent_iterator, urgent_views); + root_for_each_container(find_urgent_iterator, urgent_views); list_stable_sort(urgent_views, cmp_urgent); struct sway_view *target; if (criteria->urgent == 'o') { // oldest @@ -228,17 +227,7 @@ list_t *criteria_get_views(struct criteria *criteria) { .criteria = criteria, .matches = matches, }; - container_for_each_descendant(&root_container, - criteria_get_views_iterator, &data); - - // Scratchpad items which are hidden are not in the tree. - for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { - struct sway_container *con = - root_container.sway_root->scratchpad->items[i]; - if (!con->parent) { - criteria_get_views_iterator(con, &data); - } - } + root_for_each_container(criteria_get_views_iterator, &data); return matches; } diff --git a/sway/debug-tree.c b/sway/debug-tree.c index f3465afe..ea0826b9 100644 --- a/sway/debug-tree.c +++ b/sway/debug-tree.c @@ -3,8 +3,10 @@ #include <wlr/render/wlr_texture.h> #include <wlr/util/log.h> #include "config.h" +#include "sway/debug.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/output.h" #include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" @@ -12,6 +14,8 @@ #include "config.h" #include "pango.h" +struct sway_debug debug; + static const char *layout_to_str(enum sway_container_layout layout) { switch (layout) { case L_HORIZ: @@ -22,8 +26,6 @@ static const char *layout_to_str(enum sway_container_layout layout) { return "L_STACKED"; case L_TABBED: return "L_TABBED"; - case L_FLOATING: - return "L_FLOATING"; case L_NONE: return "L_NONE"; } @@ -69,10 +71,8 @@ static int draw_container(cairo_t *cairo, struct sway_container *container, return height; } -bool enable_debug_tree = false; - void update_debug_tree() { - if (!enable_debug_tree) { + if (!debug.render_tree) { return; } diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index 6575519d..72650397 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -22,3 +22,21 @@ void desktop_damage_whole_container(struct sway_container *con) { } } } + +void desktop_damage_box(struct wlr_box *box) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *cont = root_container.children->items[i]; + output_damage_box(cont->sway_output, box); + } +} + +void desktop_damage_view(struct sway_view *view) { + desktop_damage_whole_container(view->swayc); + struct wlr_box box = { + .x = view->swayc->current.view_x - view->geometry.x, + .y = view->swayc->current.view_y - view->geometry.y, + .width = view->surface->current.width, + .height = view->surface->current.height, + }; + desktop_damage_box(&box); +} diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 66747a3f..3d8bbff5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -144,15 +144,16 @@ void output_view_for_each_surface(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, - .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, + .ox = view->swayc->current.view_x - output->swayc->current.swayc_x + - view->geometry.x, + .oy = view->swayc->current.view_y - output->swayc->current.swayc_y + - view->geometry.y, .width = view->swayc->current.view_width, .height = view->swayc->current.view_height, .rotation = 0, // TODO }; - view_for_each_surface(view, - output_for_each_surface_iterator, &data); + view_for_each_surface(view, output_for_each_surface_iterator, &data); } void output_view_for_each_popup(struct sway_output *output, @@ -162,8 +163,10 @@ void output_view_for_each_popup(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, - .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, + .ox = view->swayc->current.view_x - output->swayc->current.swayc_x + - view->geometry.x, + .oy = view->swayc->current.view_y - output->swayc->current.swayc_y + - view->geometry.y, .width = view->swayc->current.view_width, .height = view->swayc->current.view_height, .rotation = 0, // TODO @@ -303,44 +306,33 @@ struct send_frame_done_data { static void send_frame_done_container_iterator(struct sway_container *con, void *_data) { - struct send_frame_done_data *data = _data; - if (!sway_assert(con->type == C_VIEW, "expected a view")) { + if (con->type != C_VIEW) { return; } - if (!view_is_visible(con->sway_view)) { return; } + struct send_frame_done_data *data = _data; output_view_for_each_surface(data->output, con->sway_view, send_frame_done_iterator, data->when); } -static void send_frame_done_container(struct sway_output *output, - struct sway_container *con, struct timespec *when) { - struct send_frame_done_data data = { - .output = output, - .when = when, - }; - container_descendants(con, C_VIEW, - send_frame_done_container_iterator, &data); -} - static void send_frame_done(struct sway_output *output, struct timespec *when) { if (output_has_opaque_overlay_layer_surface(output)) { goto send_frame_overlay; } + struct send_frame_done_data data = { + .output = output, + .when = when, + }; struct sway_container *workspace = output_get_active_workspace(output); if (workspace->current.ws_fullscreen) { - if (workspace->current.ws_fullscreen->type == C_VIEW) { - output_view_for_each_surface(output, - workspace->current.ws_fullscreen->sway_view, - send_frame_done_iterator, when); - } else { - send_frame_done_container(output, workspace->current.ws_fullscreen, - when); - } + send_frame_done_container_iterator( + workspace->current.ws_fullscreen, &data); + container_for_each_child(workspace->current.ws_fullscreen, + send_frame_done_container_iterator, &data); #ifdef HAVE_XWAYLAND send_frame_done_unmanaged(output, &root_container.sway_root->xwayland_unmanaged, when); @@ -351,9 +343,8 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_done_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], when); - send_frame_done_container(output, workspace, when); - send_frame_done_container(output, workspace->sway_workspace->floating, - when); + workspace_for_each_container(workspace, + send_frame_done_container_iterator, &data); #ifdef HAVE_XWAYLAND send_frame_done_unmanaged(output, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index cdac9c72..5cf8abc0 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -193,9 +193,11 @@ static void render_view_toplevels(struct sway_view *view, .alpha = alpha, }; // Render all toplevels without descending into popups - output_surface_for_each_surface(output, view->surface, - view->swayc->current.view_x - output->wlr_output->lx, - view->swayc->current.view_y - output->wlr_output->ly, + double ox = + view->swayc->current.view_x - output->wlr_output->lx - view->geometry.x; + double oy = + view->swayc->current.view_y - output->wlr_output->ly - view->geometry.y; + output_surface_for_each_surface(output, view->surface, ox, oy, render_surface_iterator, &data); } @@ -227,8 +229,10 @@ static void render_saved_view(struct sway_view *view, 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, + .x = view->swayc->current.view_x - output->swayc->current.swayc_x - + view->saved_geometry.x, + .y = view->swayc->current.view_y - output->swayc->current.swayc_y - + view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, }; @@ -266,7 +270,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, render_view_toplevels(view, output, damage, view->swayc->alpha); } - if (view->using_csd) { + if (view->swayc->current.using_csd) { return; } @@ -585,7 +589,7 @@ static void render_container_simple(struct sway_output *output, marks_texture = view->marks_unfocused; } - if (!view->using_csd) { + if (!view->swayc->current.using_csd) { if (state->border == B_NORMAL) { render_titlebar(output, damage, child, state->swayc_x, state->swayc_y, state->swayc_width, colors, @@ -750,8 +754,6 @@ static void render_container(struct sway_output *output, case L_TABBED: render_container_tabbed(output, damage, con, parent_focused); break; - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); } } @@ -777,7 +779,7 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (!view->using_csd) { + if (!view->swayc->current.using_csd) { if (con->current.border == B_NORMAL) { render_titlebar(soutput, damage, con, con->current.swayc_x, con->current.swayc_y, con->current.swayc_width, colors, @@ -802,8 +804,7 @@ static void render_floating(struct sway_output *soutput, if (!workspace_is_visible(ws)) { continue; } - list_t *floating = - ws->current.ws_floating->current.children; + list_t *floating = ws->current.ws_floating; for (int k = 0; k < floating->length; ++k) { struct sway_container *floater = floating->items[k]; render_floating_container(soutput, damage, floater); @@ -812,8 +813,6 @@ static void render_floating(struct sway_output *soutput, } } -const char *damage_debug = NULL; - void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; @@ -827,21 +826,17 @@ void output_render(struct sway_output *output, struct timespec *when, wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); - bool damage_whole_before_swap = false; if (!pixman_region32_not_empty(damage)) { // Output isn't damaged but needs buffer swap goto renderer_end; } - if (damage_debug != NULL) { - if (strcmp(damage_debug, "highlight") == 0) { - wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); - damage_whole_before_swap = true; - } else if (strcmp(damage_debug, "rerender") == 0) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(damage, damage, 0, 0, width, height); - } + if (debug.damage == DAMAGE_HIGHLIGHT) { + wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); + } else if (debug.damage == DAMAGE_RERENDER) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(damage, damage, 0, 0, width, height); } struct sway_container *workspace = output_get_active_workspace(output); @@ -915,12 +910,12 @@ render_overlay: render_drag_icons(output, damage, &root_container.sway_root->drag_icons); renderer_end: - if (root_container.sway_root->debug_tree) { + if (debug.render_tree) { + wlr_renderer_scissor(renderer, NULL); wlr_render_texture(renderer, root_container.sway_root->debug_tree, - wlr_output->transform_matrix, 0, 0, 1); + wlr_output->transform_matrix, 0, 40, 1); } - - if (damage_whole_before_swap || root_container.sway_root->debug_tree) { + if (debug.damage == DAMAGE_HIGHLIGHT) { int width, height; wlr_output_transformed_resolution(wlr_output, &width, &height); pixman_region32_union_rect(damage, damage, 0, 0, width, height); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index c08730ce..f82e5ef2 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -1,11 +1,13 @@ #define _POSIX_C_SOURCE 200809L #include <errno.h> +#include <limits.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <wlr/types/wlr_buffer.h> #include "sway/debug.h" +#include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/output.h" @@ -15,26 +17,12 @@ #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. - */ -int txn_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. - */ -bool txn_debug = false; - struct sway_transaction { struct wl_event_source *timer; list_t *instructions; // struct sway_transaction_instruction * size_t num_waiting; size_t num_configures; uint32_t con_ids; // Bitwise XOR of view container IDs - struct timespec create_time; struct timespec commit_time; }; @@ -52,9 +40,6 @@ static struct sway_transaction *transaction_create() { return NULL; } transaction->instructions = create_list(); - if (server.debug_txn_timings) { - clock_gettime(CLOCK_MONOTONIC, &transaction->create_time); - } return transaction; } @@ -107,10 +92,12 @@ static void copy_pending_state(struct sway_container *container, state->border_left = view->border_left; state->border_right = view->border_right; state->border_bottom = view->border_bottom; + state->using_csd = view->using_csd; } else if (container->type == C_WORKSPACE) { state->ws_fullscreen = container->sway_workspace->fullscreen; - state->ws_floating = container->sway_workspace->floating; + state->ws_floating = create_list(); state->children = create_list(); + list_cat(state->ws_floating, container->sway_workspace->floating); list_cat(state->children, container->children); } else { state->children = create_list(); @@ -147,19 +134,14 @@ static void transaction_add_container(struct sway_transaction *transaction, */ static void transaction_apply(struct sway_transaction *transaction) { wlr_log(WLR_DEBUG, "Applying transaction %p", transaction); - if (server.debug_txn_timings) { + if (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 + + float ms = (now.tv_sec - commit->tv_sec) * 1000 + (now.tv_nsec - commit->tv_nsec) / 1000000.0; - float ms_total = ms_arranging + ms_waiting; - wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " - "%.1fms total (%.1f frames if 60Hz)", transaction, - ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60)); + wlr_log(WLR_DEBUG, "Transaction %p: %.1fms waiting " + "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60)); } // Apply the instruction state to the container's current state @@ -168,25 +150,17 @@ static void transaction_apply(struct sway_transaction *transaction) { 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.current.children->length; ++j) { - struct sway_container *output = root_container.current.children->items[j]; - if (output->sway_output) { - output_damage_box(output->sway_output, &old_box); - output_damage_box(output->sway_output, &new_box); - } + // Damage the old location + desktop_damage_whole_container(container); + if (container->type == C_VIEW && container->sway_view->saved_buffer) { + struct sway_view *view = container->sway_view; + struct wlr_box box = { + .x = container->current.view_x - view->saved_geometry.x, + .y = container->current.view_y - view->saved_geometry.y, + .width = view->saved_buffer_width, + .height = view->saved_buffer_height, + }; + desktop_damage_box(&box); } // There are separate children lists for each instruction state, the @@ -195,15 +169,35 @@ static void transaction_apply(struct sway_transaction *transaction) { // Any child containers which are being deleted will be cleaned up in // transaction_destroy(). list_free(container->current.children); + list_free(container->current.ws_floating); memcpy(&container->current, &instruction->state, sizeof(struct sway_container_state)); if (container->type == C_VIEW && container->sway_view->saved_buffer) { - view_remove_saved_buffer(container->sway_view); + if (!container->destroying || container->ntxnrefs == 1) { + view_remove_saved_buffer(container->sway_view); + } + } + + // Damage the new location + desktop_damage_whole_container(container); + if (container->type == C_VIEW && container->sway_view->surface) { + struct sway_view *view = container->sway_view; + struct wlr_surface *surface = view->surface; + struct wlr_box box = { + .x = container->current.view_x - view->geometry.x, + .y = container->current.view_y - view->geometry.y, + .width = surface->current.width, + .height = surface->current.height, + }; + desktop_damage_box(&box); } container->instruction = NULL; + if (container->type == C_CONTAINER || container->type == C_VIEW) { + container_discover_outputs(container); + } } } @@ -294,31 +288,38 @@ static void transaction_commit(struct sway_transaction *transaction) { struct timespec when; wlr_surface_send_frame_done(con->sway_view->surface, &when); } - if (con->type == C_VIEW) { + if (con->type == C_VIEW && !con->sway_view->saved_buffer) { view_save_buffer(con->sway_view); + memcpy(&con->sway_view->saved_geometry, &con->sway_view->geometry, + sizeof(struct wlr_box)); } con->instruction = instruction; } transaction->num_configures = transaction->num_waiting; - if (server.debug_txn_timings) { + if (debug.txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } + if (debug.noatomic) { + transaction->num_waiting = 0; + } else if (debug.txn_wait) { + // Force the transaction to time out even if all views are ready. + // We do this by inflating the waiting counter. + transaction->num_waiting += 1000000; + } 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); if (transaction->timer) { - wl_event_source_timer_update(transaction->timer, txn_timeout_ms); + wl_event_source_timer_update(transaction->timer, + server.txn_timeout_ms); } else { wlr_log(WLR_ERROR, "Unable to create transaction timer (%s). " "Some imperfect frames might be rendered.", strerror(errno)); - handle_timeout(transaction); + transaction->num_waiting = 0; } - } else { - wlr_log(WLR_DEBUG, - "Transaction %p has nothing to wait for", transaction); } // The debug tree shows the pending/live tree. Here is a good place to @@ -331,7 +332,7 @@ static void set_instruction_ready( struct sway_transaction_instruction *instruction) { struct sway_transaction *transaction = instruction->transaction; - if (server.debug_txn_timings) { + if (debug.txn_timings) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct timespec *start = &transaction->commit_time; @@ -342,15 +343,12 @@ static void set_instruction_ready( transaction->num_configures - transaction->num_waiting + 1, transaction->num_configures, ms, instruction->container->name); - } // If the transaction has timed out then its num_waiting will be 0 already. if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { - if (!txn_debug) { - wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); - wl_event_source_timer_update(transaction->timer, 0); - } + wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); + wl_event_source_timer_update(transaction->timer, 0); } instruction->container->instruction = NULL; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 6a7a3f7f..aae129bd 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -7,6 +7,7 @@ #include <wlr/util/edges.h> #include "log.h" #include "sway/decoration.h" +#include "sway/desktop.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/server.h" @@ -107,7 +108,8 @@ static void get_constraints(struct sway_view *view, double *min_width, *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; } -static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_string_prop(struct sway_view *view, + enum sway_view_prop prop) { if (xdg_shell_view_from_view(view) == NULL) { return NULL; } @@ -255,8 +257,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { } if (view->swayc->instruction) { + wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); transaction_notify_view_ready_by_serial(view, xdg_surface->configure_serial); + } else { + struct wlr_box new_geo; + wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); + + if ((new_geo.width != view->width || new_geo.height != view->height) && + container_is_floating(view->swayc)) { + // A floating view has unexpectedly sent a new size + desktop_damage_view(view); + view_update_size(view, new_geo.width, new_geo.height); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + desktop_damage_view(view); + transaction_commit_dirty(); + } else { + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + } } view_damage_from(view); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 5b3c7b2b..277c53a3 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -6,6 +6,7 @@ #include <wlr/types/wlr_xdg_shell_v6.h> #include "log.h" #include "sway/decoration.h" +#include "sway/desktop.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/server.h" @@ -106,7 +107,8 @@ static void get_constraints(struct sway_view *view, double *min_width, *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; } -static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_string_prop(struct sway_view *view, + enum sway_view_prop prop) { if (xdg_shell_v6_view_from_view(view) == NULL) { return NULL; } @@ -250,9 +252,26 @@ static void handle_commit(struct wl_listener *listener, void *data) { if (!view->swayc) { return; } + if (view->swayc->instruction) { + wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); transaction_notify_view_ready_by_serial(view, xdg_surface_v6->configure_serial); + } else { + struct wlr_box new_geo; + wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); + + if ((new_geo.width != view->width || new_geo.height != view->height) && + container_is_floating(view->swayc)) { + // A floating view has unexpectedly sent a new size + desktop_damage_view(view); + view_update_size(view, new_geo.width, new_geo.height); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + desktop_damage_view(view); + transaction_commit_dirty(); + } else { + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + } } view_damage_from(view); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 14f59b9c..ce7235e4 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -277,18 +277,44 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; +static void get_geometry(struct sway_view *view, struct wlr_box *box) { + box->x = box->y = 0; + if (view->surface) { + box->width = view->surface->current.width; + box->height = view->surface->current.height; + } else { + box->width = 0; + box->height = 0; + } +} + static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit); 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; + struct wlr_surface_state *state = &xsurface->surface->current; if (view->swayc->instruction) { + get_geometry(view, &view->geometry); transaction_notify_view_ready_by_size(view, - surface_state->width, surface_state->height); - } else if (container_is_floating(view->swayc)) { - view_update_size(view, surface_state->width, surface_state->height); + state->width, state->height); + } else { + struct wlr_box new_geo; + get_geometry(view, &new_geo); + + if ((new_geo.width != view->width || new_geo.height != view->height) && + container_is_floating(view->swayc)) { + // A floating view has unexpectedly sent a new size + // eg. The Firefox "Save As" dialog when downloading a file + desktop_damage_view(view); + view_update_size(view, new_geo.width, new_geo.height); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + desktop_damage_view(view); + transaction_commit_dirty(); + } else { + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + } } view_damage_from(view); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3b70b471..ba5e0400 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -215,6 +215,19 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont, return edge; } +static void handle_down_motion(struct sway_seat *seat, + struct sway_cursor *cursor, uint32_t time_msec) { + struct sway_container *con = seat->op_container; + if (seat_is_input_allowed(seat, con->sway_view->surface)) { + double moved_x = cursor->cursor->x - seat->op_ref_lx; + double moved_y = cursor->cursor->y - seat->op_ref_ly; + double sx = seat->op_ref_con_lx + moved_x; + double sy = seat->op_ref_con_ly + moved_y; + wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); + } + seat->op_moved = true; +} + static void handle_move_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; @@ -397,6 +410,9 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, if (seat->operation != OP_NONE) { switch (seat->operation) { + case OP_DOWN: + handle_down_motion(seat, cursor, time_msec); + break; case OP_MOVE: handle_move_motion(seat, cursor); break; @@ -708,7 +724,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || on_titlebar)) { - while (cont->parent->layout != L_FLOATING) { + while (cont->parent->type != C_WORKSPACE) { cont = cont->parent; } seat_begin_move(seat, cont, button); @@ -726,13 +742,13 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } // Via mod+click - struct sway_container *floater = cont; - while (floater->parent->layout != L_FLOATING) { - floater = floater->parent; - } uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { + if (mod_pressed && button == btn_resize) { + struct sway_container *floater = cont; + while (floater->parent->type != C_WORKSPACE) { + floater = floater->parent; + } edge = 0; edge |= cursor->cursor->x > floater->x + floater->width / 2 ? WLR_EDGE_RIGHT : WLR_EDGE_LEFT; @@ -743,6 +759,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } } + // Handle mousedown on a container surface + if (surface && cont && state == WLR_BUTTON_PRESSED) { + seat_set_focus(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); + seat_begin_down(seat, cont, button, sx, sy); + return; + } + // Handle clicking a container surface if (cont) { seat_set_focus(seat, cont); diff --git a/sway/input/seat.c b/sway/input/seat.c index 57cc65f6..4077a8dd 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -124,42 +124,6 @@ static void seat_send_focus(struct sway_container *con, } } -static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, - struct sway_container *container, enum sway_container_type type, - bool only_tiling) { - if (container->type == C_VIEW) { - return container; - } - - struct sway_container *floating = - container->type == C_WORKSPACE && !only_tiling ? - container->sway_workspace->floating : NULL; - if (container->children->length == 0 && - (!floating || floating->children->length == 0)) { - return container; - } - - struct sway_seat_container *current = NULL; - wl_list_for_each(current, &seat->focus_stack, link) { - if (current->container->type != type && type != C_TYPES) { - continue; - } - - if (container_has_ancestor(current->container, container)) { - if (only_tiling && - container_is_floating_or_child(current->container)) { - continue; - } - return current->container; - } - if (floating && container_has_ancestor(current->container, floating)) { - return current->container; - } - } - - return NULL; -} - void seat_focus_inactive_children_for_each(struct sway_seat *seat, struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { @@ -175,8 +139,18 @@ void seat_focus_inactive_children_for_each(struct sway_seat *seat, } struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, - struct sway_container *container) { - return seat_get_focus_by_type(seat, container, C_VIEW, false); + struct sway_container *ancestor) { + if (ancestor->type == C_VIEW) { + return ancestor; + } + struct sway_seat_container *current; + wl_list_for_each(current, &seat->focus_stack, link) { + struct sway_container *con = current->container; + if (con->type == C_VIEW && container_has_ancestor(con, ancestor)) { + return con; + } + } + return NULL; } static void handle_seat_container_destroy(struct wl_listener *listener, @@ -198,7 +172,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener, if (set_focus) { struct sway_container *next_focus = NULL; while (next_focus == NULL) { - next_focus = seat_get_focus_by_type(seat, parent, C_VIEW, false); + next_focus = seat_get_focus_inactive_view(seat, parent); if (next_focus == NULL && parent->type == C_WORKSPACE) { next_focus = parent; @@ -339,9 +313,6 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) { static void collect_focus_iter(struct sway_container *con, void *data) { struct sway_seat *seat = data; - if (con->type > C_WORKSPACE) { - return; - } struct sway_seat_container *seat_con = seat_container_from_container(seat, con); if (!seat_con) { @@ -375,7 +346,8 @@ struct sway_seat *seat_create(struct sway_input_manager *input, // init the focus stack wl_list_init(&seat->focus_stack); - container_for_each_descendant(&root_container, collect_focus_iter, seat); + root_for_each_workspace(collect_focus_iter, seat); + root_for_each_container(collect_focus_iter, seat); wl_signal_add(&root_container.sway_root->events.new_container, &seat->new_container); @@ -653,8 +625,7 @@ void seat_set_focus_warp(struct sway_seat *seat, // find new output's old workspace, which might have to be removed if empty struct sway_container *new_output_last_ws = NULL; if (last_output && new_output && last_output != new_output) { - new_output_last_ws = - seat_get_focus_by_type(seat, new_output, C_WORKSPACE, false); + new_output_last_ws = seat_get_active_child(seat, new_output); } if (container && container->parent) { @@ -717,7 +688,8 @@ void seat_set_focus_warp(struct sway_seat *seat, // If we've focused a floating container, bring it to the front. // We do this by putting it at the end of the floating list. if (container && container_is_floating(container)) { - list_move_to_end(container->parent->children, container); + list_move_to_end( + container->parent->sway_workspace->floating, container); } // clean up unfocused empty workspace on new output @@ -877,22 +849,66 @@ void seat_set_exclusive_client(struct sway_seat *seat, } struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, - struct sway_container *container) { - return seat_get_focus_by_type(seat, container, C_TYPES, false); + struct sway_container *con) { + if (con->type == C_WORKSPACE && !con->children->length && + !con->sway_workspace->floating->length) { + return con; + } + if (con->type == C_VIEW) { + return con; + } + struct sway_seat_container *current; + wl_list_for_each(current, &seat->focus_stack, link) { + if (container_has_ancestor(current->container, con)) { + return current->container; + } + } + return NULL; } struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, - struct sway_container *container) { - return seat_get_focus_by_type(seat, container, C_TYPES, true); + struct sway_container *ancestor) { + if (ancestor->type == C_WORKSPACE && !ancestor->children->length) { + return ancestor; + } + struct sway_seat_container *current; + wl_list_for_each(current, &seat->focus_stack, link) { + struct sway_container *con = current->container; + if (!container_is_floating_or_child(con) && + container_has_ancestor(current->container, ancestor)) { + return con; + } + } + return NULL; +} + +struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, + struct sway_container *ancestor) { + if (ancestor->type == C_WORKSPACE && + !ancestor->sway_workspace->floating->length) { + return NULL; + } + struct sway_seat_container *current; + wl_list_for_each(current, &seat->focus_stack, link) { + struct sway_container *con = current->container; + if (container_is_floating_or_child(con) && + container_has_ancestor(current->container, ancestor)) { + return con; + } + } + return NULL; } struct sway_container *seat_get_active_child(struct sway_seat *seat, - struct sway_container *container) { - struct sway_seat_container *current = NULL; + struct sway_container *parent) { + if (parent->type == C_VIEW) { + return parent; + } + struct sway_seat_container *current; wl_list_for_each(current, &seat->focus_stack, link) { - if (current->container->parent == container && - current->container->layout != L_FLOATING) { - return current->container; + struct sway_container *con = current->container; + if (con->parent == parent) { + return con; } } return NULL; @@ -902,7 +918,9 @@ struct sway_container *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; } - return seat_get_focus_inactive(seat, &root_container); + struct sway_seat_container *current = + wl_container_of(seat->focus_stack.next, current, link); + return current->container; } void seat_apply_config(struct sway_seat *seat, @@ -930,6 +948,18 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } +void seat_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t button, double sx, double sy) { + seat->operation = OP_DOWN; + seat->op_container = con; + seat->op_button = button; + seat->op_ref_lx = seat->cursor->cursor->x; + seat->op_ref_ly = seat->cursor->cursor->y; + seat->op_ref_con_lx = sx; + seat->op_ref_con_ly = sy; + seat->op_moved = false; +} + void seat_begin_move(struct sway_seat *seat, struct sway_container *con, uint32_t button) { if (!seat->cursor) { @@ -954,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; @@ -983,6 +1013,7 @@ void seat_begin_resize_tiling(struct sway_seat *seat, } void seat_end_mouse_operation(struct sway_seat *seat) { + enum sway_seat_operation operation = seat->operation; if (seat->operation == OP_MOVE) { // We "move" the container to its own location so it discovers its // output again. @@ -991,7 +1022,19 @@ void seat_end_mouse_operation(struct sway_seat *seat) { } seat->operation = OP_NONE; seat->op_container = NULL; - cursor_set_image(seat->cursor, "left_ptr", NULL); + if (operation == OP_DOWN) { + // Set the cursor's previous coords to the x/y at the start of the + // operation, so the container change will be detected if using + // focus_follows_mouse and the cursor moved off the original container + // during the operation. + seat->cursor->previous.x = seat->op_ref_lx; + seat->cursor->previous.y = seat->op_ref_ly; + if (seat->op_moved) { + cursor_send_pointer_motion(seat->cursor, 0, true); + } + } else { + cursor_set_image(seat->cursor, "left_ptr", NULL); + } } void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 4c2bcc98..06cb7e11 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" @@ -23,8 +24,6 @@ static const char *ipc_json_layout_description(enum sway_container_layout l) { return "tabbed"; case L_STACKED: return "stacked"; - case L_FLOATING: - return "floating"; case L_NONE: break; } @@ -180,10 +179,11 @@ static void ipc_json_describe_workspace(struct sway_container *workspace, // Floating json_object *floating_array = json_object_new_array(); - struct sway_container *floating = workspace->sway_workspace->floating; - for (int i = 0; i < floating->children->length; ++i) { - struct sway_container *floater = floating->children->items[i]; - json_object_array_add(floating_array, ipc_json_describe_container_recursive(floater)); + list_t *floating = workspace->sway_workspace->floating; + for (int i = 0; i < floating->length; ++i) { + struct sway_container *floater = floating->items[i]; + json_object_array_add(floating_array, + ipc_json_describe_container_recursive(floater)); } json_object_object_add(object, "floating_nodes", floating_array); } @@ -193,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))); diff --git a/sway/ipc-server.c b/sway/ipc-server.c index dad1f310..34e940ad 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -522,7 +522,7 @@ void ipc_client_disconnect(struct ipc_client *client) { static void ipc_get_workspaces_callback(struct sway_container *workspace, void *data) { - if (workspace->type != C_WORKSPACE) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { return; } json_object *workspace_json = ipc_json_describe_container(workspace); @@ -631,8 +631,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_WORKSPACES: { json_object *workspaces = json_object_new_array(); - container_for_each_descendant(&root_container, - ipc_get_workspaces_callback, workspaces); + root_for_each_workspace(ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); client_valid = ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); @@ -729,8 +728,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_MARKS: { json_object *marks = json_object_new_array(); - container_descendants(&root_container, C_VIEW, ipc_get_marks_callback, - marks); + root_for_each_container(ipc_get_marks_callback, marks); const char *json_string = json_object_to_json_string(marks); client_valid = ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); diff --git a/sway/main.c b/sway/main.c index 54f48340..3ba4ba75 100644 --- a/sway/main.c +++ b/sway/main.c @@ -235,14 +235,20 @@ static void drop_permissions(bool keep_caps) { } void enable_debug_flag(const char *flag) { - if (strcmp(flag, "render-tree") == 0) { - enable_debug_tree = true; - } else if (strncmp(flag, "damage=", 7) == 0) { - damage_debug = &flag[7]; - } else if (strcmp(flag, "txn-debug") == 0) { - txn_debug = true; + if (strcmp(flag, "damage=highlight") == 0) { + debug.damage = DAMAGE_HIGHLIGHT; + } else if (strcmp(flag, "damage=rerender") == 0) { + debug.damage = DAMAGE_RERENDER; + } else if (strcmp(flag, "noatomic") == 0) { + debug.noatomic = true; + } else if (strcmp(flag, "render-tree") == 0) { + debug.render_tree = true; + } else if (strcmp(flag, "txn-wait") == 0) { + debug.txn_wait = true; + } else if (strcmp(flag, "txn-timings") == 0) { + debug.txn_timings = true; } else if (strncmp(flag, "txn-timeout=", 12) == 0) { - txn_timeout_ms = atoi(&flag[12]); + server.txn_timeout_ms = atoi(&flag[12]); } } diff --git a/sway/meson.build b/sway/meson.build index 2a457270..676422d0 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -64,6 +64,7 @@ sway_sources = files( 'commands/mouse_warping.c', 'commands/move.c', 'commands/no_focus.c', + 'commands/nop.c', 'commands/output.c', 'commands/reload.c', 'commands/rename.c', diff --git a/sway/server.c b/sway/server.c index e8dc63be..00acaa01 100644 --- a/sway/server.c +++ b/sway/server.c @@ -128,10 +128,11 @@ 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; + // This may have been set already via -Dtxn-timeout + if (!server->txn_timeout_ms) { + server->txn_timeout_ms = 200; } + server->dirty_containers = create_list(); server->transactions = create_list(); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 70b74a45..83188067 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -51,7 +51,7 @@ The following commands may only be used in the configuration file. *wordexp*(3) for details). The same include file can only be included once; subsequent attempts will be ignored. -*set* <name> <value> +*set* $<name> <value> Sets variable $_name_ to _value_. You can use the new variable in the arguments of future commands. @@ -132,7 +132,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). If unspecified, the default is 10 pixels. Pixels are ignored when moving tiled containers. -*move* [absolute] position <pos_x> [px] <pos_y> [px] +*move* [absolute] position <pos\_x> [px] <pos\_y> [px] Moves the focused container to the specified position. *move* [absolute] position center|mouse @@ -154,7 +154,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). Moves the focused container to the previous or next workspace on this output, wrapping around if already at the first or last workspace. -*move* container|window [to] workspace back_and_forth +*move* container|window [to] workspace back\_and\_forth Moves the focused container to previously focused workspace. *move* container|window|workspace [to] output <name> @@ -167,6 +167,10 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *move* [to] scratchpad Moves the focused window to the scratchpad. +*nop* <comment> + A no operation command that can be used to override default behaviour. The + optional comment argument is ignored, but logged for debugging purposes. + *reload* Reloads the sway config file and applies any changes. @@ -215,13 +219,20 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). The following commands may be used either in the configuration file or at runtime. -*assign* <criteria> [→] <workspace> +*assign* <criteria> [→] [workspace] [number] <workspace> Assigns views matching _criteria_ (see *CRITERIA* for details) to _workspace_. The → (U+2192) is optional and cosmetic. This command is equivalent to: for\_window <criteria> move container to workspace <workspace> +*assign* <criteria> [→] output left|right|up|down|<name> + Assigns views matching _criteria_ (see *CRITERIA* for details) to the + specified output. The → (U+2192) is optional and cosmetic. This command is + equivalent to: + + for\_window <criteria> move container to output <output> + *bindsym* [--release|--locked] <key combo> <command> Binds _key combo_ to execute the sway command _command_ when pressed. You may use XKB key names here (*xev*(1) is a good tool for discovering these). diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 494a8461..a4b058f3 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -144,9 +144,9 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { static void arrange_children_of(struct sway_container *parent); -static void arrange_floating(struct sway_container *floating) { - for (int i = 0; i < floating->children->length; ++i) { - struct sway_container *floater = floating->children->items[i]; +static void arrange_floating(list_t *floating) { + for (int i = 0; i < floating->length; ++i) { + struct sway_container *floater = floating->items[i]; if (floater->type == C_VIEW) { view_autoconfigure(floater->sway_view); } else { @@ -154,7 +154,6 @@ static void arrange_floating(struct sway_container *floating) { } container_set_dirty(floater); } - container_set_dirty(floating); } static void arrange_children_of(struct sway_container *parent) { @@ -179,9 +178,6 @@ static void arrange_children_of(struct sway_container *parent) { case L_NONE: apply_horiz_layout(parent); break; - case L_FLOATING: - arrange_floating(parent); - break; } // Recurse into child containers @@ -210,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, diff --git a/sway/tree/container.c b/sway/tree/container.c index eb06edc2..a8c6e667 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -67,32 +67,11 @@ void container_update_textures_recursive(struct sway_container *con) { } if (con->type == C_WORKSPACE) { - container_update_textures_recursive(con->sway_workspace->floating); - } - } -} - -static void handle_reparent(struct wl_listener *listener, - void *data) { - struct sway_container *container = - wl_container_of(listener, container, reparent); - struct sway_container *old_parent = data; - - struct sway_container *old_output = old_parent; - if (old_output != NULL && old_output->type != C_OUTPUT) { - old_output = container_parent(old_output, C_OUTPUT); - } - - struct sway_container *new_output = container->parent; - if (new_output != NULL && new_output->type != C_OUTPUT) { - new_output = container_parent(new_output, C_OUTPUT); - } - - if (old_output && new_output) { - float old_scale = old_output->sway_output->wlr_output->scale; - float new_scale = new_output->sway_output->wlr_output->scale; - if (old_scale != new_scale) { - container_update_textures_recursive(container); + for (int i = 0; i < con->sway_workspace->floating->length; ++i) { + struct sway_container *floater = + con->sway_workspace->floating->items[i]; + container_update_textures_recursive(floater); + } } } } @@ -113,12 +92,9 @@ struct sway_container *container_create(enum sway_container_type type) { c->children = create_list(); c->current.children = create_list(); } + c->outputs = create_list(); wl_signal_init(&c->events.destroy); - wl_signal_init(&c->events.reparent); - - wl_signal_add(&c->events.reparent, &c->reparent); - c->reparent.notify = handle_reparent; c->has_gaps = false; c->gaps_inner = 0; @@ -131,6 +107,7 @@ struct sway_container *container_create(enum sway_container_type type) { 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); } @@ -151,6 +128,7 @@ void container_free(struct sway_container *cont) { wlr_texture_destroy(cont->title_urgent); list_free(cont->children); list_free(cont->current.children); + list_free(cont->outputs); switch (cont->type) { case C_ROOT: @@ -222,18 +200,25 @@ static struct sway_container *container_workspace_destroy( for (int i = 0; i < workspace->children->length; i++) { container_move_to(workspace->children->items[i], new_workspace); } - struct sway_container *floating = workspace->sway_workspace->floating; - for (int i = 0; i < floating->children->length; i++) { - container_move_to(floating->children->items[i], - new_workspace->sway_workspace->floating); + 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); } } - container_destroy_noreaping(workspace->sway_workspace->floating); - 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")) { @@ -270,11 +255,13 @@ static struct sway_container *container_output_destroy( container_destroy(workspace); } - container_sort_workspaces(new_output); + 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); @@ -339,10 +326,6 @@ static struct sway_container *container_destroy_noreaping( } bool container_reap_empty(struct sway_container *con) { - if (con->layout == L_FLOATING) { - // Don't reap the magical floating container that each workspace has - return false; - } switch (con->type) { case C_ROOT: case C_OUTPUT: @@ -432,8 +415,10 @@ struct sway_container *container_close(struct sway_container *con) { if (con->type == C_VIEW) { view_close(con->sway_view); - } else { - container_for_each_descendant(con, container_close_func, NULL); + } else if (con->type == C_CONTAINER) { + container_for_each_child(con, container_close_func, NULL); + } else if (con->type == C_WORKSPACE) { + workspace_for_each_container(con, container_close_func, NULL); } return parent; @@ -465,23 +450,12 @@ struct sway_container *container_view_create(struct sway_container *sibling, return swayc; } -void container_descendants(struct sway_container *root, - enum sway_container_type type, - void (*func)(struct sway_container *item, void *data), void *data) { - if (!root->children || !root->children->length) { - return; - } - for (int i = 0; i < root->children->length; ++i) { - struct sway_container *item = root->children->items[i]; - if (item->type == type) { - func(item, data); - } - container_descendants(item, type, func, data); - } -} - -struct sway_container *container_find(struct sway_container *container, +struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data) { + if (!sway_assert(container->type == C_CONTAINER || + container->type == C_VIEW, "Expected a container or view")) { + return NULL; + } if (!container->children) { return NULL; } @@ -489,15 +463,11 @@ struct sway_container *container_find(struct sway_container *container, struct sway_container *child = container->children->items[i]; if (test(child, data)) { return child; - } else { - struct sway_container *res = container_find(child, test, data); - if (res) { - return res; - } } - } - if (container->type == C_WORKSPACE) { - return container_find(container->sway_workspace->floating, test, data); + struct sway_container *res = container_find_child(child, test, data); + if (res) { + return res; + } } return NULL; } @@ -522,8 +492,8 @@ static void surface_at_view(struct sway_container *swayc, double lx, double ly, return; } struct sway_view *sview = swayc->sway_view; - double view_sx = lx - sview->x; - double view_sy = ly - sview->y; + double view_sx = lx - sview->x + sview->geometry.x; + double view_sy = ly - sview->y + sview->geometry.y; double _sx, _sy; struct wlr_surface *_surface = NULL; @@ -639,9 +609,8 @@ static struct sway_container *floating_container_at(double lx, double ly, } // Items at the end of the list are on top, so iterate the list in // reverse. - for (int k = ws->floating->children->length - 1; k >= 0; --k) { - struct sway_container *floater = - ws->floating->children->items[k]; + for (int k = ws->floating->length - 1; k >= 0; --k) { + struct sway_container *floater = ws->floating->items[k]; struct wlr_box box = { .x = floater->x, .y = floater->y, @@ -677,9 +646,6 @@ struct sway_container *tiling_container_at( return container_at_tabbed(con, lx, ly, surface, sx, sy); case L_STACKED: return container_at_stacked(con, lx, ly, surface, sx, sy); - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - return NULL; case L_NONE: return NULL; } @@ -743,26 +709,20 @@ struct sway_container *container_at(struct sway_container *workspace, return NULL; } -void container_for_each_descendant(struct sway_container *container, +void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { - if (!container) { + if (!sway_assert(container->type == C_CONTAINER || + container->type == C_VIEW, "Expected a container or view")) { return; } if (container->children) { for (int i = 0; i < container->children->length; ++i) { struct sway_container *child = container->children->items[i]; - container_for_each_descendant(child, f, data); - } - } - if (container->type == C_WORKSPACE) { - struct sway_container *floating = container->sway_workspace->floating; - for (int i = 0; i < floating->children->length; ++i) { - struct sway_container *child = floating->children->items[i]; - container_for_each_descendant(child, f, data); + f(child, data); + container_for_each_child(child, f, data); } } - f(container, data); } bool container_has_ancestor(struct sway_container *descendant, @@ -800,13 +760,24 @@ void container_damage_whole(struct sway_container *container) { } } +/** + * Return the output which will be used for scale purposes. + * This is the most recently entered output. + */ +struct sway_output *container_get_effective_output(struct sway_container *con) { + if (con->outputs->length == 0) { + return NULL; + } + return con->outputs->items[con->outputs->length - 1]; +} + static void update_title_texture(struct sway_container *con, struct wlr_texture **texture, struct border_colors *class) { if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, "Unexpected type %s", container_type_to_str(con->type))) { return; } - struct sway_container *output = container_parent(con, C_OUTPUT); + struct sway_output *output = container_get_effective_output(con); if (!output) { return; } @@ -818,7 +789,7 @@ static void update_title_texture(struct sway_container *con, return; } - double scale = output->sway_output->wlr_output->scale; + double scale = output->wlr_output->scale; int width = 0; int height = con->title_height * scale; @@ -846,7 +817,7 @@ static void update_title_texture(struct sway_container *con, unsigned char *data = cairo_image_surface_get_data(surface); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); struct wlr_renderer *renderer = wlr_backend_get_renderer( - output->sway_output->wlr_output->backend); + output->wlr_output->backend); *texture = wlr_texture_from_pixels( renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); cairo_surface_destroy(surface); @@ -899,9 +870,6 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe case L_STACKED: lenient_strcat(buffer, "S["); break; - case L_FLOATING: - lenient_strcat(buffer, "F["); - break; case L_NONE: lenient_strcat(buffer, "D["); break; @@ -1030,12 +998,13 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *workspace = container_parent(container, C_WORKSPACE); if (enable) { - container_remove_child(container); - container_add_child(workspace->sway_workspace->floating, container); + struct sway_container *old_parent = container_remove_child(container); + workspace_add_floating(workspace, container); container_init_floating(container); if (container->type == C_VIEW) { view_set_tiled(container->sway_view, false); } + container_reap_empty(old_parent); } else { // Returning to tiled if (container->scratchpad) { @@ -1083,14 +1052,12 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { con->y = view->y - top; con->width = view->width + border_width * 2; con->height = top + view->height + border_width; + container_set_dirty(con); } bool container_is_floating(struct sway_container *container) { - struct sway_container *workspace = container_parent(container, C_WORKSPACE); - if (!workspace) { - return false; - } - return container->parent == workspace->sway_workspace->floating; + return container->parent && container->parent->type == C_WORKSPACE && + list_find(container->parent->sway_workspace->floating, container) != -1; } void container_get_box(struct sway_container *container, struct wlr_box *box) { @@ -1170,7 +1137,7 @@ void container_floating_move_to(struct sway_container *con, output_get_active_workspace(new_output->sway_output); if (old_workspace != new_workspace) { container_remove_child(con); - container_add_child(new_workspace->sway_workspace->floating, con); + workspace_add_floating(new_workspace, con); arrange_windows(old_workspace); arrange_windows(new_workspace); workspace_detect_urgent(old_workspace); @@ -1197,13 +1164,12 @@ void container_set_dirty(struct sway_container *container) { list_add(server.dirty_containers, container); } -static bool find_urgent_iterator(struct sway_container *con, - void *data) { +static bool find_urgent_iterator(struct sway_container *con, void *data) { return con->type == C_VIEW && view_is_urgent(con->sway_view); } bool container_has_urgent_child(struct sway_container *container) { - return container_find(container, find_urgent_iterator, NULL); + return container_find_child(container, find_urgent_iterator, NULL); } void container_end_mouse_operation(struct sway_container *container) { @@ -1235,7 +1201,8 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { container_set_fullscreen(workspace->sway_workspace->fullscreen, false); } - container_for_each_descendant(container, set_fullscreen_iterator, &enable); + set_fullscreen_iterator(container, &enable); + container_for_each_child(container, set_fullscreen_iterator, &enable); container->is_fullscreen = enable; @@ -1284,14 +1251,10 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { } bool container_is_floating_or_child(struct sway_container *container) { - do { - if (container->parent && container->parent->layout == L_FLOATING) { - return true; - } + while (container->parent && container->parent->type != C_WORKSPACE) { container = container->parent; - } while (container && container->type != C_WORKSPACE); - - return false; + } + return container_is_floating(container); } bool container_is_fullscreen_or_child(struct sway_container *container) { @@ -1305,14 +1268,66 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { return false; } -struct sway_container *container_wrap_children(struct sway_container *parent) { - struct sway_container *middle = container_create(C_CONTAINER); - middle->layout = parent->layout; - while (parent->children->length) { - struct sway_container *child = parent->children->items[0]; - container_remove_child(child); - container_add_child(middle, child); +static void surface_send_enter_iterator(struct wlr_surface *surface, + int x, int y, void *data) { + struct wlr_output *wlr_output = data; + wlr_surface_send_enter(surface, wlr_output); +} + +static void surface_send_leave_iterator(struct wlr_surface *surface, + int x, int y, void *data) { + struct wlr_output *wlr_output = data; + wlr_surface_send_leave(surface, wlr_output); +} + +void container_discover_outputs(struct sway_container *con) { + if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, + "Expected a container or view")) { + return; + } + struct wlr_box con_box = { + .x = con->current.swayc_x, + .y = con->current.swayc_y, + .width = con->current.swayc_width, + .height = con->current.swayc_height, + }; + struct sway_output *old_output = container_get_effective_output(con); + + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + struct sway_output *sway_output = output->sway_output; + struct wlr_box output_box; + container_get_box(output, &output_box); + struct wlr_box intersection; + bool intersects = + wlr_box_intersection(&con_box, &output_box, &intersection); + int index = list_find(con->outputs, sway_output); + + if (intersects && index == -1) { + // Send enter + wlr_log(WLR_DEBUG, "Con %p entered output %p", con, sway_output); + if (con->type == C_VIEW) { + view_for_each_surface(con->sway_view, + surface_send_enter_iterator, sway_output->wlr_output); + } + list_add(con->outputs, sway_output); + } else if (!intersects && index != -1) { + // Send leave + wlr_log(WLR_DEBUG, "Con %p left output %p", con, sway_output); + if (con->type == C_VIEW) { + view_for_each_surface(con->sway_view, + surface_send_leave_iterator, sway_output->wlr_output); + } + list_del(con->outputs, index); + } + } + struct sway_output *new_output = container_get_effective_output(con); + double old_scale = old_output ? old_output->wlr_output->scale : -1; + double new_scale = new_output ? new_output->wlr_output->scale : -1; + if (old_scale != new_scale) { + container_update_title_textures(con); + if (con->type == C_VIEW) { + view_update_marks_textures(con->sway_view); + } } - container_add_child(parent, middle); - return middle; } diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 2b710403..a3de44ce 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -1,5 +1,4 @@ #define _POSIX_C_SOURCE 200809L -#include <ctype.h> #include <math.h> #include <stdbool.h> #include <stdlib.h> @@ -76,7 +75,6 @@ void container_insert_child(struct sway_container *parent, list_insert(parent->children, i, child); child->parent = parent; container_handle_fullscreen_reparent(child, old_parent); - wl_signal_emit(&child->events.reparent, old_parent); } struct sway_container *container_add_sibling(struct sway_container *fixed, @@ -92,7 +90,6 @@ struct sway_container *container_add_sibling(struct sway_container *fixed, list_insert(parent->children, i + 1, active); active->parent = parent; container_handle_fullscreen_reparent(active, old_parent); - wl_signal_emit(&active->events.reparent, old_parent); return active->parent; } @@ -118,9 +115,11 @@ struct sway_container *container_remove_child(struct sway_container *child) { } struct sway_container *parent = child->parent; - int index = index_child(child); + list_t *list = container_is_floating(child) ? + parent->sway_workspace->floating : parent->children; + int index = list_find(list, child); if (index != -1) { - list_del(parent->children, index); + list_del(list, index); } child->parent = NULL; container_notify_subtree_changed(parent); @@ -161,7 +160,8 @@ void container_move_to(struct sway_container *container, struct sway_container *old_output = container_parent(container, C_OUTPUT); old_parent = container_remove_child(container); - container_add_child(new_ws->sway_workspace->floating, container); + workspace_add_floating(new_ws, container); + container_handle_fullscreen_reparent(container, old_parent); // If changing output, center it within the workspace if (old_output != new_ws->parent && !container->is_fullscreen) { container_floating_move_to_center(container); @@ -179,8 +179,6 @@ void container_move_to(struct sway_container *container, } } - wl_signal_emit(&container->events.reparent, old_parent); - if (container->type == C_VIEW) { ipc_event_window(container, "move"); } @@ -305,7 +303,6 @@ static void workspace_rejigger(struct sway_container *ws, container_flatten(ws); container_reap_empty_recursive(original_parent); - wl_signal_emit(&child->events.reparent, original_parent); container_create_notify(new_parent); } @@ -432,9 +429,6 @@ void container_move(struct sway_container *container, if ((index == parent->children->length - 1 && offs > 0) || (index == 0 && offs < 0)) { if (current->parent == container->parent) { - if (parent->parent->layout == L_FLOATING) { - return; - } if (!parent->is_fullscreen && (parent->layout == L_TABBED || parent->layout == L_STACKED)) { @@ -458,14 +452,10 @@ void container_move(struct sway_container *container, sibling = parent->children->items[index + offs]; wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); } - } else if (!parent->is_fullscreen && - parent->parent->layout != L_FLOATING && - (parent->layout == L_TABBED || + } else if (!parent->is_fullscreen && (parent->layout == L_TABBED || parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; - } else if (parent->parent->layout == L_FLOATING) { - return; } else { wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); current = current->parent; @@ -591,28 +581,6 @@ enum sway_container_layout container_get_default_layout( } } -static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { - struct sway_container *a = *(void **)_a; - struct sway_container *b = *(void **)_b; - int retval = 0; - - if (isdigit(a->name[0]) && isdigit(b->name[0])) { - int a_num = strtol(a->name, NULL, 10); - int b_num = strtol(b->name, NULL, 10); - retval = (a_num < b_num) ? -1 : (a_num > b_num); - } else if (isdigit(a->name[0])) { - retval = -1; - } else if (isdigit(b->name[0])) { - retval = 1; - } - - return retval; -} - -void container_sort_workspaces(struct sway_container *output) { - list_stable_sort(output->children, sort_workspace_cmp_qsort); -} - /** * Get swayc in the direction of newly entered output. */ @@ -825,13 +793,15 @@ struct sway_container *container_replace_child(struct sway_container *child, if (parent == NULL) { return NULL; } - int i = index_child(child); - // TODO floating + list_t *list = container_is_floating(child) ? + parent->sway_workspace->floating : parent->children; + int i = list_find(list, child); + if (new_child->parent) { container_remove_child(new_child); } - parent->children->items[i] = new_child; + list->items[i] = new_child; new_child->parent = parent; child->parent = NULL; @@ -884,7 +854,6 @@ struct sway_container *container_split(struct sway_container *child, struct sway_container *ws_child = workspace->children->items[0]; container_remove_child(ws_child); container_add_child(cont, ws_child); - wl_signal_emit(&ws_child->events.reparent, workspace); } container_add_child(workspace, cont); @@ -892,11 +861,9 @@ struct sway_container *container_split(struct sway_container *child, workspace->layout = layout; cont->layout = old_layout; } else { - struct sway_container *old_parent = child->parent; cont->layout = layout; container_replace_child(child, cont); container_add_child(cont, child); - wl_signal_emit(&child->events.reparent, old_parent); } if (set_focus) { @@ -909,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; } @@ -996,7 +963,8 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { "Cannot swap ancestor and descendant")) { return; } - if (!sway_assert(con1->layout != L_FLOATING && con2->layout != L_FLOATING, + if (!sway_assert(!container_is_floating(con1) + && !container_is_floating(con2), "Swapping with floating containers is not supported")) { return; } diff --git a/sway/tree/output.c b/sway/tree/output.c index 31e3bf9b..6da63064 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include <ctype.h> #include <string.h> #include <strings.h> #include "sway/ipc-server.h" @@ -28,7 +29,7 @@ static void restore_workspaces(struct sway_container *output) { } } - container_sort_workspaces(output); + output_sort_workspaces(output); } struct sway_container *output_create( @@ -102,3 +103,73 @@ struct sway_container *output_create( return 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")) { + return; + } + for (int i = 0; i < output->children->length; ++i) { + struct sway_container *workspace = output->children->items[i]; + f(workspace, data); + } +} + +void output_for_each_container(struct sway_container *output, + void (*f)(struct sway_container *con, void *data), void *data) { + if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { + return; + } + for (int i = 0; i < output->children->length; ++i) { + struct sway_container *workspace = output->children->items[i]; + workspace_for_each_container(workspace, f, data); + } +} + +struct sway_container *output_find_workspace(struct sway_container *output, + bool (*test)(struct sway_container *con, void *data), void *data) { + if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { + return NULL; + } + for (int i = 0; i < output->children->length; ++i) { + struct sway_container *workspace = output->children->items[i]; + if (test(workspace, data)) { + return workspace; + } + } + return NULL; +} + +struct sway_container *output_find_container(struct sway_container *output, + bool (*test)(struct sway_container *con, void *data), void *data) { + if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { + return NULL; + } + struct sway_container *result = NULL; + for (int i = 0; i < output->children->length; ++i) { + struct sway_container *workspace = output->children->items[i]; + if ((result = workspace_find_container(workspace, test, data))) { + return result; + } + } + return NULL; +} + +static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { + struct sway_container *a = *(void **)_a; + struct sway_container *b = *(void **)_b; + + if (isdigit(a->name[0]) && isdigit(b->name[0])) { + int a_num = strtol(a->name, NULL, 10); + int b_num = strtol(b->name, NULL, 10); + return (a_num < b_num) ? -1 : (a_num > b_num); + } else if (isdigit(a->name[0])) { + return -1; + } else if (isdigit(b->name[0])) { + return 1; + } + return 0; +} + +void output_sort_workspaces(struct sway_container *output) { + list_stable_sort(output->children, sort_workspace_cmp_qsort); +} diff --git a/sway/tree/root.c b/sway/tree/root.c index fc908cc1..c27ff2c3 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -105,7 +105,7 @@ void root_scratchpad_show(struct sway_container *con) { if (con->parent) { container_remove_child(con); } - container_add_child(ws->sway_workspace->floating, con); + workspace_add_floating(ws, con); // Make sure the container's center point overlaps this workspace double center_lx = con->x + con->width / 2; @@ -256,3 +256,81 @@ void root_record_workspace_pid(pid_t pid) { &pw->output_destroy); wl_list_insert(&pid_workspaces, &pw->link); } + +void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), + void *data) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + output_for_each_workspace(output, f, data); + } +} + +void root_for_each_container(void (*f)(struct sway_container *con, void *data), + void *data) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + output_for_each_container(output, f, data); + } + + // Scratchpad + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *container = + root_container.sway_root->scratchpad->items[i]; + // If the container has a parent then it's visible on a workspace + // and will have been iterated in the previous for loop. So we only + // iterate the hidden scratchpad containers here. + if (!container->parent) { + f(container, data); + container_for_each_child(container, f, data); + } + } +} + +struct sway_container *root_find_output( + bool (*test)(struct sway_container *con, void *data), void *data) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + if (test(output, data)) { + return output; + } + } + return NULL; +} + +struct sway_container *root_find_workspace( + bool (*test)(struct sway_container *con, void *data), void *data) { + struct sway_container *result = NULL; + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + if ((result = output_find_workspace(output, test, data))) { + return result; + } + } + return NULL; +} + +struct sway_container *root_find_container( + bool (*test)(struct sway_container *con, void *data), void *data) { + struct sway_container *result = NULL; + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + if ((result = output_find_container(output, test, data))) { + return result; + } + } + + // Scratchpad + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *container = + root_container.sway_root->scratchpad->items[i]; + if (!container->parent) { + if (test(container, data)) { + return container; + } + if ((result = container_find_child(container, test, data))) { + return result; + } + } + } + return NULL; +} diff --git a/sway/tree/view.c b/sway/tree/view.c index 950494d8..7bf7325a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -236,7 +236,12 @@ void view_autoconfigure(struct sway_view *view) { view->border_top = false; } - switch (view->border) { + enum sway_container_border border = view->border; + if (view->using_csd) { + border = B_NONE; + } + + switch (border) { case B_NONE: x = con->x; y = con->y + y_offset; @@ -364,48 +369,6 @@ static void view_handle_surface_new_subsurface(struct wl_listener *listener, view_subsurface_create(view, subsurface); } -static void surface_send_enter_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_enter(surface, wlr_output); -} - -static void surface_send_leave_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_leave(surface, wlr_output); -} - -static void view_handle_container_reparent(struct wl_listener *listener, - void *data) { - struct sway_view *view = - wl_container_of(listener, view, container_reparent); - struct sway_container *old_parent = data; - - struct sway_container *old_output = old_parent; - if (old_output != NULL && old_output->type != C_OUTPUT) { - old_output = container_parent(old_output, C_OUTPUT); - } - - struct sway_container *new_output = view->swayc->parent; - if (new_output != NULL && new_output->type != C_OUTPUT) { - new_output = container_parent(new_output, C_OUTPUT); - } - - if (old_output == new_output) { - return; - } - - if (old_output != NULL) { - view_for_each_surface(view, surface_send_leave_iterator, - old_output->sway_output->wlr_output); - } - if (new_output != NULL) { - view_for_each_surface(view, surface_send_enter_iterator, - new_output->sway_output->wlr_output); - } -} - static bool view_has_executed_criteria(struct sway_view *view, struct criteria *criteria) { for (int i = 0; i < view->executed_criteria->length; ++i) { @@ -450,12 +413,22 @@ static struct sway_container *select_workspace(struct sway_view *view) { // Check if there's any `assign` criteria for the view list_t *criterias = criteria_for_view(view, - CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); + CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); struct sway_container *ws = NULL; for (int i = 0; i < criterias->length; ++i) { struct criteria *criteria = criterias->items[i]; - if (criteria->type == CT_ASSIGN_WORKSPACE) { - ws = workspace_by_name(criteria->target); + if (criteria->type == CT_ASSIGN_OUTPUT) { + struct sway_container *output = output_by_name(criteria->target); + if (output) { + ws = seat_get_active_child(seat, output); + break; + } + } else { + // CT_ASSIGN_WORKSPACE(_NUMBER) + ws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ? + workspace_by_number(criteria->target) : + workspace_by_name(criteria->target); + if (!ws) { if (strcasecmp(criteria->target, "back_and_forth") == 0) { if (prev_workspace_name) { @@ -466,13 +439,6 @@ static struct sway_container *select_workspace(struct sway_view *view) { } } break; - } else { - // CT_ASSIGN_OUTPUT - struct sway_container *output = output_by_name(criteria->target); - if (output) { - ws = seat_get_active_child(seat, output); - break; - } } } list_free(criterias); @@ -528,7 +494,7 @@ static bool should_focus(struct sway_view *view) { struct sway_container *parent = view->swayc->parent; if (parent->type == C_WORKSPACE && prev_focus == parent) { size_t num_children = parent->children->length + - parent->sway_workspace->floating->children->length; + parent->sway_workspace->floating->length; if (num_children == 1) { return true; } @@ -554,7 +520,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { // If we're about to launch the view into the floating container, then // launch it as a tiled view in the root of the workspace instead. if (container_is_floating(target_sibling)) { - target_sibling = target_sibling->parent->parent; + target_sibling = target_sibling->parent; } view->swayc = container_view_create(target_sibling, view); @@ -564,9 +530,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { &view->surface_new_subsurface); view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; - wl_signal_add(&view->swayc->events.reparent, &view->container_reparent); - view->container_reparent.notify = view_handle_container_reparent; - if (view->impl->wants_floating && view->impl->wants_floating(view)) { view->border = config->floating_border; view->border_thickness = config->floating_border_thickness; @@ -584,15 +547,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_update_title(view, false); container_notify_subtree_changed(view->swayc->parent); view_execute_criteria(view); - - view_handle_container_reparent(&view->container_reparent, NULL); } void 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->urgent_timer) { wl_event_source_remove(view->urgent_timer); @@ -615,34 +575,16 @@ void view_unmap(struct sway_view *view) { view->surface = NULL; } -void view_update_position(struct sway_view *view, double lx, double ly) { - if (view->x == lx && view->y == ly) { - return; - } - container_damage_whole(view->swayc); - view->x = lx; - view->y = ly; - view->swayc->current.view_x = lx; - view->swayc->current.view_y = ly; - if (container_is_floating(view->swayc)) { - container_set_geometry_from_floating_view(view->swayc); - } - container_damage_whole(view->swayc); -} - void view_update_size(struct sway_view *view, int width, int height) { - if (view->width == width && view->height == height) { + if (!sway_assert(container_is_floating(view->swayc), + "Expected a floating container")) { return; } - container_damage_whole(view->swayc); view->width = width; view->height = height; view->swayc->current.view_width = width; view->swayc->current.view_height = height; - if (container_is_floating(view->swayc)) { - container_set_geometry_from_floating_view(view->swayc); - } - container_damage_whole(view->swayc); + container_set_geometry_from_floating_view(view->swayc); } static void view_subsurface_create(struct sway_view *view, @@ -899,8 +841,8 @@ static bool find_by_mark_iterator(struct sway_container *con, } struct sway_view *view_find_mark(char *mark) { - struct sway_container *container = container_find(&root_container, - find_by_mark_iterator, mark); + struct sway_container *container = root_find_container( + find_by_mark_iterator, mark); if (!container) { return NULL; } @@ -908,7 +850,7 @@ struct sway_view *view_find_mark(char *mark) { } bool view_find_and_unmark(char *mark) { - struct sway_container *container = container_find(&root_container, + struct sway_container *container = root_find_container( find_by_mark_iterator, mark); if (!container) { return false; @@ -952,7 +894,7 @@ void view_add_mark(struct sway_view *view, char *mark) { static void update_marks_texture(struct sway_view *view, struct wlr_texture **texture, struct border_colors *class) { - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + struct sway_output *output = container_get_effective_output(view->swayc); if (!output) { return; } @@ -988,7 +930,7 @@ static void update_marks_texture(struct sway_view *view, } free(part); - double scale = output->sway_output->wlr_output->scale; + double scale = output->wlr_output->scale; int width = 0; int height = view->swayc->title_height * scale; @@ -1014,7 +956,7 @@ static void update_marks_texture(struct sway_view *view, unsigned char *data = cairo_image_surface_get_data(surface); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); struct wlr_renderer *renderer = wlr_backend_get_renderer( - output->sway_output->wlr_output->backend); + output->wlr_output->backend); *texture = wlr_texture_from_pixels( renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); cairo_surface_destroy(surface); @@ -1061,7 +1003,7 @@ bool view_is_visible(struct sway_view *view) { // Check view isn't in a tabbed or stacked container on an inactive tab struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *container = view->swayc; - while (container->type != C_WORKSPACE && container->layout != L_FLOATING) { + while (container->type != C_WORKSPACE) { if (container->parent->layout == L_TABBED || container->parent->layout == L_STACKED) { if (seat_get_active_child(seat, container->parent) != container) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index cd2a7a04..cf50ee09 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -67,26 +67,19 @@ struct sway_container *workspace_create(struct sway_container *output, return NULL; } swayws->swayc = workspace; - swayws->floating = container_create(C_CONTAINER); - swayws->floating->parent = swayws->swayc; - swayws->floating->layout = L_FLOATING; + swayws->floating = create_list(); swayws->output_priority = create_list(); workspace->sway_workspace = swayws; workspace_output_add_priority(workspace, output); container_add_child(output, workspace); - container_sort_workspaces(output); + output_sort_workspaces(output); container_create_notify(workspace); return workspace; } char *prev_workspace_name = NULL; -struct workspace_by_number_data { - int len; - const char *cset; - const char *name; -}; void next_name_map(struct sway_container *ws, void *data) { int *count = data; @@ -154,7 +147,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); // Make sure the workspace number doesn't already exist - if (workspace_by_number(_target)) { + if (isdigit(_target[0]) && workspace_by_number(_target)) { free(_target); free(dup); return; @@ -212,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) { @@ -233,19 +227,18 @@ static bool _workspace_by_number(struct sway_container *view, void *data) { if (view->type != C_WORKSPACE) { return false; } - struct workspace_by_number_data *wbnd = data; - int a = strspn(view->name, wbnd->cset); - return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0; + char *name = data; + char *view_name = view->name; + while (isdigit(*name)) { + if (*name++ != *view_name++) { + return false; + } + } + return !isdigit(*view_name); } struct sway_container *workspace_by_number(const char* name) { - struct workspace_by_number_data wbnd = {0, "1234567890", name}; - wbnd.len = strspn(name, wbnd.cset); - if (wbnd.len <= 0) { - return NULL; - } - return container_find(&root_container, - _workspace_by_number, (void *) &wbnd); + return root_find_workspace(_workspace_by_number, (void *) name); } static bool _workspace_by_name(struct sway_container *view, void *data) { @@ -258,7 +251,8 @@ struct sway_container *workspace_by_name(const char *name) { struct sway_container *current_workspace = NULL, *current_output = NULL; struct sway_container *focus = seat_get_focus(seat); if (focus) { - current_workspace = container_parent(focus, C_WORKSPACE); + current_workspace = focus->type == C_WORKSPACE ? + focus : container_parent(focus, C_WORKSPACE); current_output = container_parent(focus, C_OUTPUT); } @@ -273,11 +267,11 @@ struct sway_container *workspace_by_name(const char *name) { } else if (strcmp(name, "current") == 0) { return current_workspace; } else if (strcasecmp(name, "back_and_forth") == 0) { - return prev_workspace_name ? container_find(&root_container, - _workspace_by_name, (void *)prev_workspace_name) : NULL; + return prev_workspace_name ? + root_find_workspace(_workspace_by_name, (void*)prev_workspace_name) + : NULL; } else { - return container_find(&root_container, _workspace_by_name, - (void *)name); + return root_find_workspace(_workspace_by_name, (void*)name); } } @@ -397,17 +391,15 @@ bool workspace_switch(struct sway_container *workspace, struct sway_container *next_output = workspace->parent; struct sway_container *next_output_prev_ws = seat_get_active_child(seat, next_output); - struct sway_container *floating = - next_output_prev_ws->sway_workspace->floating; + list_t *floating = next_output_prev_ws->sway_workspace->floating; bool has_sticky = false; if (workspace != next_output_prev_ws) { - for (int i = 0; i < floating->children->length; ++i) { - struct sway_container *floater = floating->children->items[i]; + for (int i = 0; i < floating->length; ++i) { + struct sway_container *floater = floating->items[i]; if (floater->is_sticky) { has_sticky = true; container_remove_child(floater); - container_add_child(workspace->sway_workspace->floating, - floater); + workspace_add_floating(workspace, floater); if (floater == focus) { seat_set_focus(seat, NULL); seat_set_focus(seat, floater); @@ -460,9 +452,9 @@ bool workspace_is_empty(struct sway_container *ws) { return false; } // Sticky views are not considered to be part of this workspace - struct sway_container *floating = ws->sway_workspace->floating; - for (int i = 0; i < floating->children->length; ++i) { - struct sway_container *floater = floating->children->items[i]; + list_t *floating = ws->sway_workspace->floating; + for (int i = 0; i < floating->length; ++i) { + struct sway_container *floater = floating->items[i]; if (!floater->is_sticky) { return false; } @@ -517,8 +509,7 @@ struct sway_container *workspace_output_get_highest_available( continue; } - struct sway_container *output = container_find(&root_container, - _output_by_name, name); + struct sway_container *output = root_find_output(_output_by_name, name); if (output) { return output; } @@ -527,8 +518,13 @@ struct sway_container *workspace_output_get_highest_available( return NULL; } +static bool find_urgent_iterator(struct sway_container *con, void *data) { + return con->type == C_VIEW && view_is_urgent(con->sway_view); +} + void workspace_detect_urgent(struct sway_container *workspace) { - bool new_urgent = container_has_urgent_child(workspace); + bool new_urgent = (bool)workspace_find_container(workspace, + find_urgent_iterator, NULL); if (workspace->sway_workspace->urgent != new_urgent) { workspace->sway_workspace->urgent = new_urgent; @@ -536,3 +532,79 @@ void workspace_detect_urgent(struct sway_container *workspace) { container_damage_whole(workspace); } } + +void workspace_for_each_container(struct sway_container *ws, + void (*f)(struct sway_container *con, void *data), void *data) { + if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { + return; + } + // Tiling + for (int i = 0; i < ws->children->length; ++i) { + struct sway_container *container = ws->children->items[i]; + f(container, data); + container_for_each_child(container, f, data); + } + // Floating + for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { + struct sway_container *container = + ws->sway_workspace->floating->items[i]; + f(container, data); + container_for_each_child(container, f, data); + } +} + +struct sway_container *workspace_find_container(struct sway_container *ws, + bool (*test)(struct sway_container *con, void *data), void *data) { + if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { + return NULL; + } + struct sway_container *result = NULL; + // Tiling + for (int i = 0; i < ws->children->length; ++i) { + struct sway_container *child = ws->children->items[i]; + if (test(child, data)) { + return child; + } + if ((result = container_find_child(child, test, data))) { + return result; + } + } + // Floating + for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { + struct sway_container *child = ws->sway_workspace->floating->items[i]; + if (test(child, data)) { + return child; + } + if ((result = container_find_child(child, test, data))) { + return result; + } + } + return NULL; +} + +struct sway_container *workspace_wrap_children(struct sway_container *ws) { + struct sway_container *middle = container_create(C_CONTAINER); + middle->layout = ws->layout; + while (ws->children->length) { + struct sway_container *child = ws->children->items[0]; + container_remove_child(child); + container_add_child(middle, child); + } + container_add_child(ws, middle); + return middle; +} + +void workspace_add_floating(struct sway_container *workspace, + struct sway_container *con) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return; + } + if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { + return; + } + + list_add(workspace->sway_workspace->floating, con); + con->parent = workspace; + container_set_dirty(workspace); + container_set_dirty(con); +} |