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.
master
Ryan Dwyer 6 years ago
parent e86d99acd6
commit f4bc25bcc6

@ -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);

@ -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

@ -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);

@ -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 ?

@ -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;

@ -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);

@ -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);
}

@ -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);

@ -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")) {

Loading…
Cancel
Save