From f4bc25bcc6c822e264938447940b7d75fa84319b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 25 Aug 2018 12:09:42 +1000 Subject: [PATCH] Relocate container_move, container_move_to and container_get_in_direction * container_move is only called from the move command * container_move_to was called from both the move command and the sticky command, but the sticky command can easily not call it * container_get_in_direction is only called from the focus command Moving these functions to their respective commands gives better separation of code and removes bloat from layout.c. These functions will need to be refactored to take advantage of type safety, so separating them will make this easier to refactor. The following static functions have also been moved: * is_parellel * invert_movement * move_offs * container_limit * workspace_rejigger * move_out_of_tabs_stacks * get_swayc_in_output_direction They were all used by the move functions, except for the last one which is used by focus. Other changes: * index_child has been renamed to container_sibling_index, moved to container.c and made public * sway_output_from_wlr has been renamed to output_from_wlr_output, moved to output.c and made public * container_handle_fullscreen_reparent has been made public * sway_dir_to_wlr has been made public No changes have been made to any of the moved functions, other than updating calls to functions that have been renamed. --- include/sway/output.h | 2 + include/sway/tree/container.h | 2 + include/sway/tree/layout.h | 15 +- sway/commands/focus.c | 193 +++++++++++ sway/commands/move.c | 406 ++++++++++++++++++++++ sway/commands/sticky.c | 5 +- sway/tree/container.c | 5 + sway/tree/layout.c | 627 +--------------------------------- sway/tree/output.c | 13 + 9 files changed, 637 insertions(+), 631 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 098540fb6..651fdfe77 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -45,6 +45,8 @@ void output_destroy(struct sway_container *output); void output_begin_destroy(struct sway_container *output); +struct sway_container *output_from_wlr_output(struct wlr_output *output); + typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *box, float rotation, void *user_data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 2cb23d3c8..2cedb6139 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -335,4 +335,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con); void container_discover_outputs(struct sway_container *con); +int container_sibling_index(const struct sway_container *child); + #endif diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 519189d94..5c834ad26 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -19,9 +19,15 @@ enum wlr_edges; struct sway_container; +void container_handle_fullscreen_reparent(struct sway_container *con, + struct sway_container *old_parent); + void container_add_child(struct sway_container *parent, struct sway_container *child); +void container_insert_child(struct sway_container *parent, + struct sway_container *child, int i); + struct sway_container *container_add_sibling(struct sway_container *parent, struct sway_container *child); @@ -30,18 +36,11 @@ struct sway_container *container_remove_child(struct sway_container *child); struct sway_container *container_replace_child(struct sway_container *child, struct sway_container *new_child); -void container_move_to(struct sway_container* container, - struct sway_container* destination); - -void container_move(struct sway_container *container, - enum movement_direction dir, int move_amt); +bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out); enum sway_container_layout container_get_default_layout( struct sway_container *con); -struct sway_container *container_get_in_direction(struct sway_container - *container, struct sway_seat *seat, enum movement_direction dir); - struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout); diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 6659a6834..a9fa9a0f8 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -31,6 +31,199 @@ static bool parse_movement_direction(const char *name, return true; } +/** + * Get swayc in the direction of newly entered output. + */ +static struct sway_container *get_swayc_in_output_direction( + struct sway_container *output, enum movement_direction dir, + struct sway_seat *seat) { + if (!output) { + return NULL; + } + + struct sway_container *ws = seat_get_focus_inactive(seat, output); + if (ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + + if (ws == NULL) { + wlr_log(WLR_ERROR, "got an output without a workspace"); + return NULL; + } + + if (ws->children->length > 0) { + switch (dir) { + case MOVE_LEFT: + if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { + // get most right child of new output + return ws->children->items[ws->children->length-1]; + } else { + return seat_get_focus_inactive(seat, ws); + } + case MOVE_RIGHT: + if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { + // get most left child of new output + return ws->children->items[0]; + } else { + return seat_get_focus_inactive(seat, ws); + } + case MOVE_UP: + case MOVE_DOWN: { + struct sway_container *focused = + seat_get_focus_inactive(seat, ws); + if (focused && focused->parent) { + struct sway_container *parent = focused->parent; + if (parent->layout == L_VERT) { + if (dir == MOVE_UP) { + // get child furthest down on new output + int idx = parent->children->length - 1; + return parent->children->items[idx]; + } else if (dir == MOVE_DOWN) { + // get child furthest up on new output + return parent->children->items[0]; + } + } + return focused; + } + break; + } + default: + break; + } + } + + return ws; +} + +static struct sway_container *container_get_in_direction( + struct sway_container *container, struct sway_seat *seat, + enum movement_direction dir) { + struct sway_container *parent = container->parent; + + if (dir == MOVE_CHILD) { + return seat_get_focus_inactive(seat, container); + } + if (container->is_fullscreen) { + if (dir == MOVE_PARENT) { + return NULL; + } + container = container_parent(container, C_OUTPUT); + parent = container->parent; + } else { + if (dir == MOVE_PARENT) { + if (parent->type == C_OUTPUT || container_is_floating(container)) { + return NULL; + } else { + return parent; + } + } + } + + struct sway_container *wrap_candidate = NULL; + while (true) { + bool can_move = false; + int desired; + int idx = list_find(container->parent->children, container); + if (idx == -1) { + return NULL; + } + if (parent->type == C_ROOT) { + enum wlr_direction wlr_dir = 0; + if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), + "got invalid direction: %d", dir)) { + return NULL; + } + int lx = container->x + container->width / 2; + int ly = container->y + container->height / 2; + struct wlr_output_layout *layout = + root_container.sway_root->output_layout; + struct wlr_output *wlr_adjacent = + wlr_output_layout_adjacent_output(layout, wlr_dir, + container->sway_output->wlr_output, lx, ly); + struct sway_container *adjacent = + output_from_wlr_output(wlr_adjacent); + + if (!adjacent || adjacent == container) { + if (!wrap_candidate) { + return NULL; + } + return seat_get_focus_inactive_view(seat, wrap_candidate); + } + struct sway_container *next = + get_swayc_in_output_direction(adjacent, dir, seat); + if (next == NULL) { + return NULL; + } + struct sway_container *next_workspace = next; + if (next_workspace->type != C_WORKSPACE) { + next_workspace = container_parent(next_workspace, C_WORKSPACE); + } + sway_assert(next_workspace, "Next container has no workspace"); + if (next_workspace->sway_workspace->fullscreen) { + return seat_get_focus_inactive(seat, + next_workspace->sway_workspace->fullscreen); + } + if (next->children && next->children->length) { + // TODO consider floating children as well + return seat_get_focus_inactive_view(seat, next); + } else { + return next; + } + } else { + if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { + if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { + can_move = true; + desired = idx + (dir == MOVE_LEFT ? -1 : 1); + } + } else { + if (parent->layout == L_VERT || parent->layout == L_STACKED) { + can_move = true; + desired = idx + (dir == MOVE_UP ? -1 : 1); + } + } + } + + if (can_move) { + // TODO handle floating + if (desired < 0 || desired >= parent->children->length) { + can_move = false; + int len = parent->children->length; + if (config->focus_wrapping != WRAP_NO && !wrap_candidate + && len > 1) { + if (desired < 0) { + wrap_candidate = parent->children->items[len-1]; + } else { + wrap_candidate = parent->children->items[0]; + } + if (config->focus_wrapping == WRAP_FORCE) { + return seat_get_focus_inactive_view(seat, + wrap_candidate); + } + } + } else { + struct sway_container *desired_con = + parent->children->items[desired]; + wlr_log(WLR_DEBUG, + "cont %d-%p dir %i sibling %d: %p", idx, + container, dir, desired, desired_con); + return seat_get_focus_inactive_view(seat, desired_con); + } + } + + if (!can_move) { + container = parent; + parent = parent->parent; + if (!parent) { + // wrapping is the last chance + if (!wrap_candidate) { + return NULL; + } + return seat_get_focus_inactive_view(seat, wrap_candidate); + } + } + } +} + static struct cmd_results *focus_mode(struct sway_container *con, struct sway_seat *seat, bool floating) { struct sway_container *ws = con->type == C_WORKSPACE ? diff --git a/sway/commands/move.c b/sway/commands/move.c index 4c0189ec7..51e52665b 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -52,6 +52,412 @@ static struct sway_container *output_in_direction(const char *direction, return output_by_name(direction); } +static void container_move_to(struct sway_container *container, + struct sway_container *destination) { + if (!sway_assert(container->type == C_CONTAINER || + container->type == C_VIEW, "Expected a container or view")) { + return; + } + if (container == destination + || container_has_ancestor(container, destination)) { + return; + } + struct sway_container *old_parent = NULL; + struct sway_container *new_parent = NULL; + if (container_is_floating(container)) { + // Resolve destination into a workspace + struct sway_container *new_ws = NULL; + if (destination->type == C_OUTPUT) { + new_ws = output_get_active_workspace(destination->sway_output); + } else if (destination->type == C_WORKSPACE) { + new_ws = destination; + } else { + new_ws = container_parent(destination, C_WORKSPACE); + } + if (!new_ws) { + // This can happen if the user has run "move container to mark foo", + // where mark foo is on a hidden scratchpad container. + return; + } + struct sway_container *old_output = + container_parent(container, C_OUTPUT); + old_parent = container_remove_child(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); + } + } else { + old_parent = container_remove_child(container); + container->width = container->height = 0; + container->saved_width = container->saved_height = 0; + + if (destination->type == C_VIEW) { + new_parent = container_add_sibling(destination, container); + } else { + new_parent = destination; + container_add_child(destination, container); + } + } + + if (container->type == C_VIEW) { + ipc_event_window(container, "move"); + } + container_notify_subtree_changed(old_parent); + container_notify_subtree_changed(new_parent); + + // If view was moved to a fullscreen workspace, refocus the fullscreen view + struct sway_container *new_workspace = container; + if (new_workspace->type != C_WORKSPACE) { + new_workspace = container_parent(new_workspace, C_WORKSPACE); + } + if (new_workspace->sway_workspace->fullscreen) { + struct sway_seat *seat; + struct sway_container *focus, *focus_ws; + wl_list_for_each(seat, &input_manager->seats, link) { + focus = seat_get_focus(seat); + focus_ws = focus; + if (focus_ws->type != C_WORKSPACE) { + focus_ws = container_parent(focus_ws, C_WORKSPACE); + } + if (focus_ws == new_workspace) { + struct sway_container *new_focus = seat_get_focus_inactive(seat, + new_workspace->sway_workspace->fullscreen); + seat_set_focus(seat, new_focus); + } + } + } + // Update workspace urgent state + struct sway_container *old_workspace = old_parent; + if (old_workspace->type != C_WORKSPACE) { + old_workspace = container_parent(old_workspace, C_WORKSPACE); + } + if (new_workspace != old_workspace) { + workspace_detect_urgent(new_workspace); + if (old_workspace) { + workspace_detect_urgent(old_workspace); + } + } +} + +static bool is_parallel(enum sway_container_layout layout, + enum movement_direction dir) { + switch (layout) { + case L_TABBED: + case L_HORIZ: + return dir == MOVE_LEFT || dir == MOVE_RIGHT; + case L_STACKED: + case L_VERT: + return dir == MOVE_UP || dir == MOVE_DOWN; + default: + return false; + } +} + +static enum movement_direction invert_movement(enum movement_direction dir) { + switch (dir) { + case MOVE_LEFT: + return MOVE_RIGHT; + case MOVE_RIGHT: + return MOVE_LEFT; + case MOVE_UP: + return MOVE_DOWN; + case MOVE_DOWN: + return MOVE_UP; + default: + sway_assert(0, "This function expects left|right|up|down"); + return MOVE_LEFT; + } +} + +static int move_offs(enum movement_direction move_dir) { + return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; +} + +/* Gets the index of the most extreme member based on the movement offset */ +static int container_limit(struct sway_container *container, + enum movement_direction move_dir) { + return move_offs(move_dir) < 0 ? 0 : container->children->length; +} + +/* Takes one child, sets it aside, wraps the rest of the children in a new + * container, switches the layout of the workspace, and drops the child back in. + * In other words, rejigger it. */ +static void workspace_rejigger(struct sway_container *ws, + struct sway_container *child, enum movement_direction move_dir) { + struct sway_container *original_parent = child->parent; + struct sway_container *new_parent = + container_split(ws, ws->layout); + + container_remove_child(child); + for (int i = 0; i < ws->children->length; ++i) { + struct sway_container *_child = ws->children->items[i]; + container_move_to(new_parent, _child); + } + + int index = move_offs(move_dir); + container_insert_child(ws, child, index < 0 ? 0 : 1); + ws->layout = + move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; + + container_flatten(ws); + container_reap_empty(original_parent); + container_create_notify(new_parent); +} + +static void move_out_of_tabs_stacks(struct sway_container *container, + struct sway_container *current, enum movement_direction move_dir, + int offs) { + if (container->parent == current->parent + && current->parent->children->length == 1) { + wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); + current->parent->layout = move_dir == + MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; + return; + } + + wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); + bool is_workspace = current->parent->type == C_WORKSPACE; + struct sway_container *new_parent = container_split(current->parent, + move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); + if (is_workspace) { + container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1); + } else { + container_insert_child(new_parent, container, offs < 0 ? 0 : 1); + container_reap_empty(new_parent->parent); + container_flatten(new_parent->parent); + } + container_create_notify(new_parent); + container_notify_subtree_changed(new_parent); +} + +static void container_move(struct sway_container *container, + enum movement_direction move_dir, int move_amt) { + if (!sway_assert( + container->type != C_CONTAINER || container->type != C_VIEW, + "Can only move containers and views")) { + return; + } + int offs = move_offs(move_dir); + + struct sway_container *sibling = NULL; + struct sway_container *current = container; + struct sway_container *parent = current->parent; + struct sway_container *top = &root_container; + + // If moving a fullscreen view, only consider outputs + if (container->is_fullscreen) { + current = container_parent(container, C_OUTPUT); + } else if (container_is_fullscreen_or_child(container) || + container_is_floating_or_child(container)) { + // If we've fullscreened a split container, only allow the child to move + // around within the fullscreen parent. + // Same with floating a split container. + struct sway_container *ws = container_parent(container, C_WORKSPACE); + top = ws->sway_workspace->fullscreen; + } + + struct sway_container *new_parent = container_flatten(parent); + if (new_parent != parent) { + // Special case: we were the last one in this container, so leave + return; + } + + while (!sibling) { + if (current == top) { + return; + } + + parent = current->parent; + wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, + container_type_to_str(current->type), current->name); + + int index = container_sibling_index(current); + + switch (current->type) { + case C_OUTPUT: { + enum wlr_direction wlr_dir = 0; + if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), + "got invalid direction: %d", move_dir)) { + return; + } + double ref_lx = current->x + current->width / 2; + double ref_ly = current->y + current->height / 2; + struct wlr_output *next = wlr_output_layout_adjacent_output( + root_container.sway_root->output_layout, wlr_dir, + current->sway_output->wlr_output, ref_lx, ref_ly); + if (!next) { + wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); + return; + } + struct sway_output *next_output = next->data; + current = next_output->swayc; + wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); + // Select workspace and get outta here + current = seat_get_focus_inactive( + config->handler_context.seat, current); + if (current->type != C_WORKSPACE) { + current = container_parent(current, C_WORKSPACE); + } + sibling = current; + break; + } + case C_WORKSPACE: + if (!is_parallel(current->layout, move_dir)) { + if (current->children->length >= 2) { + wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", + current->children->length); + workspace_rejigger(current, container, move_dir); + return; + } else { + wlr_log(WLR_DEBUG, "Selecting output"); + current = current->parent; + } + } else if (current->layout == L_TABBED + || current->layout == L_STACKED) { + wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); + workspace_rejigger(current, container, move_dir); + } else { + wlr_log(WLR_DEBUG, "Selecting output"); + current = current->parent; + } + break; + case C_CONTAINER: + case C_VIEW: + if (is_parallel(parent->layout, move_dir)) { + if ((index == parent->children->length - 1 && offs > 0) + || (index == 0 && offs < 0)) { + if (current->parent == container->parent) { + if (!parent->is_fullscreen && + (parent->layout == L_TABBED || + parent->layout == L_STACKED)) { + move_out_of_tabs_stacks(container, current, + move_dir, offs); + return; + } else { + wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); + current = current->parent; + } + } else { + wlr_log(WLR_DEBUG, "Hit limit, " + "promoting descendant to sibling"); + // Special case + container_insert_child(current->parent, container, + index + (offs < 0 ? 0 : 1)); + container->width = container->height = 0; + return; + } + } else { + sibling = parent->children->items[index + offs]; + wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); + } + } 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 { + wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); + current = current->parent; + } + break; + default: + sway_assert(0, "Not expecting to see container of type %s here", + container_type_to_str(current->type)); + return; + } + } + + // Part two: move stuff around + int index = container_sibling_index(container); + struct sway_container *old_parent = container->parent; + + while (sibling) { + switch (sibling->type) { + case C_VIEW: + if (sibling->parent == container->parent) { + wlr_log(WLR_DEBUG, "Swapping siblings"); + sibling->parent->children->items[index + offs] = container; + sibling->parent->children->items[index] = sibling; + } else { + wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); + container_insert_child(sibling->parent, container, + container_sibling_index(sibling) + (offs > 0 ? 0 : 1)); + container->width = container->height = 0; + } + sibling = NULL; + break; + case C_WORKSPACE: // Note: only in the case of moving between outputs + case C_CONTAINER: + if (is_parallel(sibling->layout, move_dir)) { + int limit = container_limit(sibling, invert_movement(move_dir)); + wlr_log(WLR_DEBUG, "limit: %d", limit); + wlr_log(WLR_DEBUG, + "Reparenting container (parallel) to index %d " + "(move dir: %d)", limit, move_dir); + container_insert_child(sibling, container, limit); + container->width = container->height = 0; + sibling = NULL; + } else { + wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); + struct sway_container *focus_inactive = seat_get_focus_inactive( + config->handler_context.seat, sibling); + if (focus_inactive && focus_inactive != sibling) { + while (focus_inactive->parent != sibling) { + focus_inactive = focus_inactive->parent; + } + wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", + focus_inactive->id); + sibling = focus_inactive; + continue; + } else if (sibling->children->length) { + wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); + container_remove_child(container); + container_add_sibling(sibling->children->items[0], container); + } else { + wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); + container_remove_child(container); + container_add_child(sibling, container); + } + container->width = container->height = 0; + sibling = NULL; + } + break; + default: + sway_assert(0, "Not expecting to see container of type %s here", + container_type_to_str(sibling->type)); + return; + } + } + + container_notify_subtree_changed(old_parent); + container_notify_subtree_changed(container->parent); + + if (container->type == C_VIEW) { + ipc_event_window(container, "move"); + } + + if (old_parent) { + seat_set_focus(config->handler_context.seat, old_parent); + seat_set_focus(config->handler_context.seat, container); + } + + struct sway_container *last_ws = old_parent; + struct sway_container *next_ws = container->parent; + if (last_ws && last_ws->type != C_WORKSPACE) { + last_ws = container_parent(last_ws, C_WORKSPACE); + } + if (next_ws && next_ws->type != C_WORKSPACE) { + next_ws = container_parent(next_ws, C_WORKSPACE); + } + if (last_ws && next_ws && last_ws != next_ws) { + ipc_event_workspace(last_ws, next_ws, "focus"); + workspace_detect_urgent(last_ws); + workspace_detect_urgent(next_ws); + } + container_end_mouse_operation(container); +} + static struct cmd_results *cmd_move_container(struct sway_container *current, int argc, char **argv) { struct cmd_results *error = NULL; diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index a0dd7215f..72ef4282e 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.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_sticky(int argc, char **argv) { @@ -44,7 +45,9 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { 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); + container_remove_child(container); + workspace_add_floating(focused_workspace, container); + container_handle_fullscreen_reparent(container, current_workspace); arrange_windows(focused_workspace); if (!container_reap_empty(current_workspace)) { arrange_windows(current_workspace); diff --git a/sway/tree/container.c b/sway/tree/container.c index 6da6212c9..5721c35c0 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1123,3 +1123,8 @@ void container_discover_outputs(struct sway_container *con) { } } } + +int container_sibling_index(const struct sway_container *child) { + return list_find(child->parent->children, child); +} + diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 12e7342bb..601d0e916 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -18,11 +18,7 @@ #include "list.h" #include "log.h" -static int index_child(const struct sway_container *child) { - return list_find(child->parent->children, child); -} - -static void container_handle_fullscreen_reparent(struct sway_container *con, +void container_handle_fullscreen_reparent(struct sway_container *con, struct sway_container *old_parent) { if (!con->is_fullscreen) { return; @@ -86,7 +82,7 @@ struct sway_container *container_add_sibling(struct sway_container *fixed, container_remove_child(active); } struct sway_container *parent = fixed->parent; - int i = index_child(fixed); + int i = container_sibling_index(fixed); list_insert(parent->children, i + 1, active); active->parent = parent; container_handle_fullscreen_reparent(active, old_parent); @@ -130,97 +126,7 @@ struct sway_container *container_remove_child(struct sway_container *child) { return parent; } -void container_move_to(struct sway_container *container, - struct sway_container *destination) { - if (!sway_assert(container->type == C_CONTAINER || - container->type == C_VIEW, "Expected a container or view")) { - return; - } - if (container == destination - || container_has_ancestor(container, destination)) { - return; - } - struct sway_container *old_parent = NULL; - struct sway_container *new_parent = NULL; - if (container_is_floating(container)) { - // Resolve destination into a workspace - struct sway_container *new_ws = NULL; - if (destination->type == C_OUTPUT) { - new_ws = output_get_active_workspace(destination->sway_output); - } else if (destination->type == C_WORKSPACE) { - new_ws = destination; - } else { - new_ws = container_parent(destination, C_WORKSPACE); - } - if (!new_ws) { - // This can happen if the user has run "move container to mark foo", - // where mark foo is on a hidden scratchpad container. - return; - } - struct sway_container *old_output = - container_parent(container, C_OUTPUT); - old_parent = container_remove_child(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); - } - } else { - old_parent = container_remove_child(container); - container->width = container->height = 0; - container->saved_width = container->saved_height = 0; - - if (destination->type == C_VIEW) { - new_parent = container_add_sibling(destination, container); - } else { - new_parent = destination; - container_add_child(destination, container); - } - } - - if (container->type == C_VIEW) { - ipc_event_window(container, "move"); - } - container_notify_subtree_changed(old_parent); - container_notify_subtree_changed(new_parent); - - // If view was moved to a fullscreen workspace, refocus the fullscreen view - struct sway_container *new_workspace = container; - if (new_workspace->type != C_WORKSPACE) { - new_workspace = container_parent(new_workspace, C_WORKSPACE); - } - if (new_workspace->sway_workspace->fullscreen) { - struct sway_seat *seat; - struct sway_container *focus, *focus_ws; - wl_list_for_each(seat, &input_manager->seats, link) { - focus = seat_get_focus(seat); - focus_ws = focus; - if (focus_ws->type != C_WORKSPACE) { - focus_ws = container_parent(focus_ws, C_WORKSPACE); - } - if (focus_ws == new_workspace) { - struct sway_container *new_focus = seat_get_focus_inactive(seat, - new_workspace->sway_workspace->fullscreen); - seat_set_focus(seat, new_focus); - } - } - } - // Update workspace urgent state - struct sway_container *old_workspace = old_parent; - if (old_workspace->type != C_WORKSPACE) { - old_workspace = container_parent(old_workspace, C_WORKSPACE); - } - if (new_workspace != old_workspace) { - workspace_detect_urgent(new_workspace); - if (old_workspace) { - workspace_detect_urgent(old_workspace); - } - } -} - -static bool sway_dir_to_wlr(enum movement_direction dir, - enum wlr_direction *out) { +bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) { switch (dir) { case MOVE_UP: *out = WLR_DIRECTION_UP; @@ -241,323 +147,6 @@ static bool sway_dir_to_wlr(enum movement_direction dir, return true; } -static bool is_parallel(enum sway_container_layout layout, - enum movement_direction dir) { - switch (layout) { - case L_TABBED: - case L_HORIZ: - return dir == MOVE_LEFT || dir == MOVE_RIGHT; - case L_STACKED: - case L_VERT: - return dir == MOVE_UP || dir == MOVE_DOWN; - default: - return false; - } -} - -static enum movement_direction invert_movement(enum movement_direction dir) { - switch (dir) { - case MOVE_LEFT: - return MOVE_RIGHT; - case MOVE_RIGHT: - return MOVE_LEFT; - case MOVE_UP: - return MOVE_DOWN; - case MOVE_DOWN: - return MOVE_UP; - default: - sway_assert(0, "This function expects left|right|up|down"); - return MOVE_LEFT; - } -} - -static int move_offs(enum movement_direction move_dir) { - return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; -} - -/* Gets the index of the most extreme member based on the movement offset */ -static int container_limit(struct sway_container *container, - enum movement_direction move_dir) { - return move_offs(move_dir) < 0 ? 0 : container->children->length; -} - -/* Takes one child, sets it aside, wraps the rest of the children in a new - * container, switches the layout of the workspace, and drops the child back in. - * In other words, rejigger it. */ -static void workspace_rejigger(struct sway_container *ws, - struct sway_container *child, enum movement_direction move_dir) { - struct sway_container *original_parent = child->parent; - struct sway_container *new_parent = - container_split(ws, ws->layout); - - container_remove_child(child); - for (int i = 0; i < ws->children->length; ++i) { - struct sway_container *_child = ws->children->items[i]; - container_move_to(new_parent, _child); - } - - int index = move_offs(move_dir); - container_insert_child(ws, child, index < 0 ? 0 : 1); - ws->layout = - move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; - - container_flatten(ws); - container_reap_empty(original_parent); - container_create_notify(new_parent); -} - -static void move_out_of_tabs_stacks(struct sway_container *container, - struct sway_container *current, enum movement_direction move_dir, - int offs) { - if (container->parent == current->parent - && current->parent->children->length == 1) { - wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); - current->parent->layout = move_dir == - MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; - return; - } - - wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); - bool is_workspace = current->parent->type == C_WORKSPACE; - struct sway_container *new_parent = container_split(current->parent, - move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); - if (is_workspace) { - container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1); - } else { - container_insert_child(new_parent, container, offs < 0 ? 0 : 1); - container_reap_empty(new_parent->parent); - container_flatten(new_parent->parent); - } - container_create_notify(new_parent); - container_notify_subtree_changed(new_parent); -} - -void container_move(struct sway_container *container, - enum movement_direction move_dir, int move_amt) { - if (!sway_assert( - container->type != C_CONTAINER || container->type != C_VIEW, - "Can only move containers and views")) { - return; - } - int offs = move_offs(move_dir); - - struct sway_container *sibling = NULL; - struct sway_container *current = container; - struct sway_container *parent = current->parent; - struct sway_container *top = &root_container; - - // If moving a fullscreen view, only consider outputs - if (container->is_fullscreen) { - current = container_parent(container, C_OUTPUT); - } else if (container_is_fullscreen_or_child(container) || - container_is_floating_or_child(container)) { - // If we've fullscreened a split container, only allow the child to move - // around within the fullscreen parent. - // Same with floating a split container. - struct sway_container *ws = container_parent(container, C_WORKSPACE); - top = ws->sway_workspace->fullscreen; - } - - struct sway_container *new_parent = container_flatten(parent); - if (new_parent != parent) { - // Special case: we were the last one in this container, so leave - return; - } - - while (!sibling) { - if (current == top) { - return; - } - - parent = current->parent; - wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, - container_type_to_str(current->type), current->name); - - int index = index_child(current); - - switch (current->type) { - case C_OUTPUT: { - enum wlr_direction wlr_dir = 0; - if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), - "got invalid direction: %d", move_dir)) { - return; - } - double ref_lx = current->x + current->width / 2; - double ref_ly = current->y + current->height / 2; - struct wlr_output *next = wlr_output_layout_adjacent_output( - root_container.sway_root->output_layout, wlr_dir, - current->sway_output->wlr_output, ref_lx, ref_ly); - if (!next) { - wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); - return; - } - struct sway_output *next_output = next->data; - current = next_output->swayc; - wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); - // Select workspace and get outta here - current = seat_get_focus_inactive( - config->handler_context.seat, current); - if (current->type != C_WORKSPACE) { - current = container_parent(current, C_WORKSPACE); - } - sibling = current; - break; - } - case C_WORKSPACE: - if (!is_parallel(current->layout, move_dir)) { - if (current->children->length >= 2) { - wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", - current->children->length); - workspace_rejigger(current, container, move_dir); - return; - } else { - wlr_log(WLR_DEBUG, "Selecting output"); - current = current->parent; - } - } else if (current->layout == L_TABBED - || current->layout == L_STACKED) { - wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); - workspace_rejigger(current, container, move_dir); - } else { - wlr_log(WLR_DEBUG, "Selecting output"); - current = current->parent; - } - break; - case C_CONTAINER: - case C_VIEW: - if (is_parallel(parent->layout, move_dir)) { - if ((index == parent->children->length - 1 && offs > 0) - || (index == 0 && offs < 0)) { - if (current->parent == container->parent) { - if (!parent->is_fullscreen && - (parent->layout == L_TABBED || - parent->layout == L_STACKED)) { - move_out_of_tabs_stacks(container, current, - move_dir, offs); - return; - } else { - wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); - current = current->parent; - } - } else { - wlr_log(WLR_DEBUG, "Hit limit, " - "promoting descendant to sibling"); - // Special case - container_insert_child(current->parent, container, - index + (offs < 0 ? 0 : 1)); - container->width = container->height = 0; - return; - } - } else { - sibling = parent->children->items[index + offs]; - wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); - } - } 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 { - wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); - current = current->parent; - } - break; - default: - sway_assert(0, "Not expecting to see container of type %s here", - container_type_to_str(current->type)); - return; - } - } - - // Part two: move stuff around - int index = index_child(container); - struct sway_container *old_parent = container->parent; - - while (sibling) { - switch (sibling->type) { - case C_VIEW: - if (sibling->parent == container->parent) { - wlr_log(WLR_DEBUG, "Swapping siblings"); - sibling->parent->children->items[index + offs] = container; - sibling->parent->children->items[index] = sibling; - } else { - wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); - container_insert_child(sibling->parent, container, - index_child(sibling) + (offs > 0 ? 0 : 1)); - container->width = container->height = 0; - } - sibling = NULL; - break; - case C_WORKSPACE: // Note: only in the case of moving between outputs - case C_CONTAINER: - if (is_parallel(sibling->layout, move_dir)) { - int limit = container_limit(sibling, invert_movement(move_dir)); - wlr_log(WLR_DEBUG, "limit: %d", limit); - wlr_log(WLR_DEBUG, - "Reparenting container (parallel) to index %d " - "(move dir: %d)", limit, move_dir); - container_insert_child(sibling, container, limit); - container->width = container->height = 0; - sibling = NULL; - } else { - wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); - struct sway_container *focus_inactive = seat_get_focus_inactive( - config->handler_context.seat, sibling); - if (focus_inactive && focus_inactive != sibling) { - while (focus_inactive->parent != sibling) { - focus_inactive = focus_inactive->parent; - } - wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", - focus_inactive->id); - sibling = focus_inactive; - continue; - } else if (sibling->children->length) { - wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); - container_remove_child(container); - container_add_sibling(sibling->children->items[0], container); - } else { - wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); - container_remove_child(container); - container_add_child(sibling, container); - } - container->width = container->height = 0; - sibling = NULL; - } - break; - default: - sway_assert(0, "Not expecting to see container of type %s here", - container_type_to_str(sibling->type)); - return; - } - } - - container_notify_subtree_changed(old_parent); - container_notify_subtree_changed(container->parent); - - if (container->type == C_VIEW) { - ipc_event_window(container, "move"); - } - - if (old_parent) { - seat_set_focus(config->handler_context.seat, old_parent); - seat_set_focus(config->handler_context.seat, container); - } - - struct sway_container *last_ws = old_parent; - struct sway_container *next_ws = container->parent; - if (last_ws && last_ws->type != C_WORKSPACE) { - last_ws = container_parent(last_ws, C_WORKSPACE); - } - if (next_ws && next_ws->type != C_WORKSPACE) { - next_ws = container_parent(next_ws, C_WORKSPACE); - } - if (last_ws && next_ws && last_ws != next_ws) { - ipc_event_workspace(last_ws, next_ws, "focus"); - workspace_detect_urgent(last_ws); - workspace_detect_urgent(next_ws); - } - container_end_mouse_operation(container); -} - enum sway_container_layout container_get_default_layout( struct sway_container *con) { if (con->type != C_OUTPUT) { @@ -581,212 +170,6 @@ enum sway_container_layout container_get_default_layout( } } -/** - * Get swayc in the direction of newly entered output. - */ -static struct sway_container *get_swayc_in_output_direction( - struct sway_container *output, enum movement_direction dir, - struct sway_seat *seat) { - if (!output) { - return NULL; - } - - struct sway_container *ws = seat_get_focus_inactive(seat, output); - if (ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } - - if (ws == NULL) { - wlr_log(WLR_ERROR, "got an output without a workspace"); - return NULL; - } - - if (ws->children->length > 0) { - switch (dir) { - case MOVE_LEFT: - if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { - // get most right child of new output - return ws->children->items[ws->children->length-1]; - } else { - return seat_get_focus_inactive(seat, ws); - } - case MOVE_RIGHT: - if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { - // get most left child of new output - return ws->children->items[0]; - } else { - return seat_get_focus_inactive(seat, ws); - } - case MOVE_UP: - case MOVE_DOWN: { - struct sway_container *focused = - seat_get_focus_inactive(seat, ws); - if (focused && focused->parent) { - struct sway_container *parent = focused->parent; - if (parent->layout == L_VERT) { - if (dir == MOVE_UP) { - // get child furthest down on new output - int idx = parent->children->length - 1; - return parent->children->items[idx]; - } else if (dir == MOVE_DOWN) { - // get child furthest up on new output - return parent->children->items[0]; - } - } - return focused; - } - break; - } - default: - break; - } - } - - return ws; -} - -static struct sway_container *sway_output_from_wlr(struct wlr_output *output) { - if (output == NULL) { - return NULL; - } - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *o = root_container.children->items[i]; - if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { - return o; - } - } - return NULL; -} - -struct sway_container *container_get_in_direction( - struct sway_container *container, struct sway_seat *seat, - enum movement_direction dir) { - struct sway_container *parent = container->parent; - - if (dir == MOVE_CHILD) { - return seat_get_focus_inactive(seat, container); - } - if (container->is_fullscreen) { - if (dir == MOVE_PARENT) { - return NULL; - } - container = container_parent(container, C_OUTPUT); - parent = container->parent; - } else { - if (dir == MOVE_PARENT) { - if (parent->type == C_OUTPUT || container_is_floating(container)) { - return NULL; - } else { - return parent; - } - } - } - - struct sway_container *wrap_candidate = NULL; - while (true) { - bool can_move = false; - int desired; - int idx = index_child(container); - if (idx == -1) { - return NULL; - } - if (parent->type == C_ROOT) { - enum wlr_direction wlr_dir = 0; - if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), - "got invalid direction: %d", dir)) { - return NULL; - } - int lx = container->x + container->width / 2; - int ly = container->y + container->height / 2; - struct wlr_output_layout *layout = - root_container.sway_root->output_layout; - struct wlr_output *wlr_adjacent = - wlr_output_layout_adjacent_output(layout, wlr_dir, - container->sway_output->wlr_output, lx, ly); - struct sway_container *adjacent = - sway_output_from_wlr(wlr_adjacent); - - if (!adjacent || adjacent == container) { - if (!wrap_candidate) { - return NULL; - } - return seat_get_focus_inactive_view(seat, wrap_candidate); - } - struct sway_container *next = - get_swayc_in_output_direction(adjacent, dir, seat); - if (next == NULL) { - return NULL; - } - struct sway_container *next_workspace = next; - if (next_workspace->type != C_WORKSPACE) { - next_workspace = container_parent(next_workspace, C_WORKSPACE); - } - sway_assert(next_workspace, "Next container has no workspace"); - if (next_workspace->sway_workspace->fullscreen) { - return seat_get_focus_inactive(seat, - next_workspace->sway_workspace->fullscreen); - } - if (next->children && next->children->length) { - // TODO consider floating children as well - return seat_get_focus_inactive_view(seat, next); - } else { - return next; - } - } else { - if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { - if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { - can_move = true; - desired = idx + (dir == MOVE_LEFT ? -1 : 1); - } - } else { - if (parent->layout == L_VERT || parent->layout == L_STACKED) { - can_move = true; - desired = idx + (dir == MOVE_UP ? -1 : 1); - } - } - } - - if (can_move) { - // TODO handle floating - if (desired < 0 || desired >= parent->children->length) { - can_move = false; - int len = parent->children->length; - if (config->focus_wrapping != WRAP_NO && !wrap_candidate - && len > 1) { - if (desired < 0) { - wrap_candidate = parent->children->items[len-1]; - } else { - wrap_candidate = parent->children->items[0]; - } - if (config->focus_wrapping == WRAP_FORCE) { - return seat_get_focus_inactive_view(seat, - wrap_candidate); - } - } - } else { - struct sway_container *desired_con = - parent->children->items[desired]; - wlr_log(WLR_DEBUG, - "cont %d-%p dir %i sibling %d: %p", idx, - container, dir, desired, desired_con); - return seat_get_focus_inactive_view(seat, desired_con); - } - } - - if (!can_move) { - container = parent; - parent = parent->parent; - if (!parent) { - // wrapping is the last chance - if (!wrap_candidate) { - return NULL; - } - return seat_get_focus_inactive_view(seat, wrap_candidate); - } - } - } -} - struct sway_container *container_replace_child(struct sway_container *child, struct sway_container *new_child) { struct sway_container *parent = child->parent; @@ -915,8 +298,8 @@ static void swap_places(struct sway_container *con1, con2->width = temp->width; con2->height = temp->height; - int temp_index = index_child(con1); - container_insert_child(con2->parent, con1, index_child(con2)); + int temp_index = container_sibling_index(con1); + container_insert_child(con2->parent, con1, container_sibling_index(con2)); container_insert_child(temp->parent, con2, temp_index); free(temp); diff --git a/sway/tree/output.c b/sway/tree/output.c index 80636c113..bfc9c7238 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -209,6 +209,19 @@ void output_begin_destroy(struct sway_container *output) { } } +struct sway_container *output_from_wlr_output(struct wlr_output *output) { + if (output == NULL) { + return NULL; + } + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *o = root_container.children->items[i]; + if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { + return o; + } + } + return NULL; +} + 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")) {