From c901510e01f7648a549187b128ed9d0f9c5627eb Mon Sep 17 00:00:00 2001
From: Brian Ashworth <bosrsf04@gmail.com>
Date: Sat, 31 Mar 2018 11:13:20 -0400
Subject: [PATCH 1/3] Fixes back-and-forth name saving for non-empty ws

---
 sway/tree/workspace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index c629f1f1..de1bf159 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -351,7 +351,7 @@ bool workspace_switch(struct sway_container *workspace) {
 	}
 	struct sway_container *active_ws = focus;
 	if (active_ws->type != C_WORKSPACE) {
-		container_parent(focus, C_WORKSPACE);
+		active_ws = container_parent(focus, C_WORKSPACE);
 	}
 
 	if (config->auto_back_and_forth

From b2d871cfe215a82266d01847f4787bbcf8c721c9 Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
Date: Sat, 31 Mar 2018 21:21:26 -0400
Subject: [PATCH 2/3] Partially implement move command

Works:

- move [container|window] to workspace <name>
    - Note, this should be able to move C_CONTAINER but this is untested
- move [workspace] to output [left|right|up|down|<name>]

Not implemented yet:

- move [left|right|up|down]
- move scratchpad
- move position
---
 include/sway/output.h         |   2 +
 include/sway/tree/container.h |   5 +
 include/sway/tree/layout.h    |  12 ++-
 sway/commands.c               |   1 +
 sway/commands/focus.c         |   7 +-
 sway/commands/move.c          | 181 ++++++++++++++++++++++++++++++++++
 sway/desktop/output.c         |   9 +-
 sway/input/keyboard.c         |   4 +-
 sway/meson.build              |   1 +
 sway/tree/container.c         |  18 ++++
 sway/tree/layout.c            |  58 ++++++-----
 sway/tree/output.c            |  11 +++
 12 files changed, 268 insertions(+), 41 deletions(-)
 create mode 100644 sway/commands/move.c

diff --git a/include/sway/output.h b/include/sway/output.h
index b4980cd8..b343ecff 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -36,4 +36,6 @@ void output_damage_whole(struct sway_output *output);
 void output_damage_whole_view(struct sway_output *output,
 	struct sway_view *view);
 
+struct sway_container *output_by_name(const char *name);
+
 #endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 6aa66da0..aff2e58e 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -84,9 +84,14 @@ struct sway_container {
 
 	struct {
 		struct wl_signal destroy;
+		// Raised after the tree updates, but before arrange_windows
+		// Passed the previous parent
+		struct wl_signal reparent;
 	} events;
 };
 
+const char *container_type_to_str(enum sway_container_type type);
+
 // TODO only one container create function and pass the type?
 struct sway_container *container_output_create(
 		struct sway_output *sway_output);
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h
index 0a904c4b..e1034657 100644
--- a/include/sway/tree/layout.h
+++ b/include/sway/tree/layout.h
@@ -11,9 +11,6 @@ enum movement_direction {
 	MOVE_DOWN,
 	MOVE_PARENT,
 	MOVE_CHILD,
-	MOVE_NEXT,
-	MOVE_PREV,
-	MOVE_FIRST
 };
 
 struct sway_container;
@@ -32,7 +29,8 @@ struct sway_root {
 
 void layout_init(void);
 
-void container_add_child(struct sway_container *parent, struct sway_container *child);
+void container_add_child(struct sway_container *parent,
+		struct sway_container *child);
 
 struct sway_container *container_add_sibling(struct sway_container *parent,
 		struct sway_container *child);
@@ -44,7 +42,11 @@ struct sway_container *container_reap_empty(struct sway_container *container);
 void container_move_to(struct sway_container* container,
 		struct sway_container* destination);
 
-enum sway_container_layout container_get_default_layout(struct sway_container *output);
+void container_move(struct sway_container *container,
+		enum movement_direction dir, int move_amt);
+
+enum sway_container_layout container_get_default_layout(
+		struct sway_container *output);
 
 void container_sort_workspaces(struct sway_container *output);
 
diff --git a/sway/commands.c b/sway/commands.c
index 90544220..b6af3d1a 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -162,6 +162,7 @@ static struct cmd_handler command_handlers[] = {
 	{ "focus", cmd_focus },
 	{ "kill", cmd_kill },
 	{ "layout", cmd_layout },
+	{ "move", cmd_move },
 	{ "reload", cmd_reload },
 };
 
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 64f079f4..0a521b9e 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -20,10 +20,6 @@ static bool parse_movement_direction(const char *name,
 		*out = MOVE_PARENT;
 	} else if (strcasecmp(name, "child") == 0) {
 		*out = MOVE_CHILD;
-	} else if (strcasecmp(name, "next") == 0) {
-		*out = MOVE_NEXT;
-	} else if (strcasecmp(name, "prev") == 0) {
-		*out = MOVE_PREV;
 	} else {
 		return false;
 	}
@@ -51,7 +47,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
 				"Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
 	}
 
-	struct sway_container *next_focus = container_get_in_direction(con, seat, direction);
+	struct sway_container *next_focus = container_get_in_direction(
+			con, seat, direction);
 	if (next_focus) {
 		sway_seat_set_focus(seat, next_focus);
 	}
diff --git a/sway/commands/move.c b/sway/commands/move.c
new file mode 100644
index 00000000..222ef314
--- /dev/null
+++ b/sway/commands/move.c
@@ -0,0 +1,181 @@
+#include <string.h>
+#include <strings.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/util/log.h>
+#include "sway/commands.h"
+#include "sway/input/seat.h"
+#include "sway/output.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/workspace.h"
+#include "stringop.h"
+#include "list.h"
+
+static const char* expected_syntax = 
+	"Expected 'move <left|right|up|down> <[px] px>' or "
+	"'move <container|window> to workspace <name>' or "
+	"'move <container|window|workspace> to output <name|direction>' or "
+	"'move position mouse'";
+
+static struct sway_container *output_in_direction(const char *direction,
+		struct wlr_output *reference, int ref_ox, int ref_oy) {
+	int ref_lx = ref_ox + reference->lx,
+		ref_ly = ref_oy + reference->ly;
+	struct {
+		char *name;
+		enum wlr_direction direction;
+	} names[] = {
+		{ "up", WLR_DIRECTION_UP },
+		{ "down", WLR_DIRECTION_DOWN },
+		{ "left", WLR_DIRECTION_LEFT },
+		{ "right", WLR_DIRECTION_RIGHT },
+	};
+	for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
+		if (strcasecmp(names[i].name, direction) == 0) {
+			struct wlr_output *adjacent = wlr_output_layout_adjacent_output(
+					root_container.sway_root->output_layout,
+					names[i].direction, reference, ref_lx, ref_ly);
+			if (adjacent) {
+				struct sway_output *sway_output = adjacent->data;
+				return sway_output->swayc;
+			}
+			break;
+		}
+	}
+	return output_by_name(direction);
+}
+
+static struct cmd_results *cmd_move_container(struct sway_container *current,
+		int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "move container/window",
+				EXPECTED_AT_LEAST, 4))) {
+		return error;
+	} else if (strcasecmp(argv[1], "to") == 0
+			&& strcasecmp(argv[2], "workspace") == 0) {
+		// move container to workspace x
+		if (current->type == C_WORKSPACE) {
+			// TODO: Wrap children in a container and move that
+			return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+		} else if (current->type != C_CONTAINER && current->type != C_VIEW) {
+			return cmd_results_new(CMD_FAILURE, "move",
+					"Can only move containers and views.");
+		}
+		struct sway_container *ws;
+		const char *num_name = NULL;
+		char *ws_name = NULL;
+		if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
+			// move "container to workspace number x"
+			num_name = argv[4];
+			ws = workspace_by_number(num_name);
+		} else {
+			ws_name = join_args(argv + 3, argc - 3);
+			ws = workspace_by_name(ws_name);
+		}
+		if (!ws) {
+			ws = workspace_create(ws_name ? ws_name : num_name);
+		}
+		free(ws_name);
+		struct sway_container *old_parent = current->parent;
+		struct sway_container *focus = sway_seat_get_focus_inactive(
+				config->handler_context.seat, ws);
+		container_move_to(current, focus);
+		sway_seat_set_focus(config->handler_context.seat, old_parent);
+		return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+	} else if (strcasecmp(argv[1], "to") == 0
+			&& strcasecmp(argv[2], "output") == 0) {
+		if (current->type == C_WORKSPACE) {
+			// TODO: Wrap children in a container and move that
+			return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+		} else if (current->type != C_CONTAINER
+				&& current->type != C_VIEW) {
+			return cmd_results_new(CMD_FAILURE, "move",
+					"Can only move containers and views.");
+		}
+		struct sway_container *source = container_parent(current, C_OUTPUT);
+		struct sway_container *destination = output_in_direction(argv[3],
+				source->sway_output->wlr_output, current->x, current->y);
+		if (!destination) {
+			return cmd_results_new(CMD_FAILURE, "move workspace",
+				"Can't find output with name/direction '%s'", argv[3]);
+		}
+		struct sway_container *focus = sway_seat_get_focus_inactive(
+				config->handler_context.seat, destination);
+		if (!focus) {
+			// We've never been to this output before
+			focus = destination->children->items[0];
+		}
+		container_move_to(current, focus);
+		return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+	}
+	return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+}
+
+static struct cmd_results *cmd_move_workspace(struct sway_container *current,
+		int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
+		return error;
+	} else if (strcasecmp(argv[1], "to") != 0
+			|| strcasecmp(argv[2], "output") != 0) {
+		return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+	}
+	struct sway_container *source = container_parent(current, C_OUTPUT);
+	int center_x = current->width / 2 + current->x,
+		center_y = current->height / 2 + current->y;
+	struct sway_container *destination = output_in_direction(argv[3],
+			source->sway_output->wlr_output, center_x, center_y);
+	if (!destination) {
+		return cmd_results_new(CMD_FAILURE, "move workspace",
+			"Can't find output with name/direction '%s'", argv[3]);
+	}
+	if (current->type != C_WORKSPACE) {
+		current = container_parent(current, C_WORKSPACE);
+	}
+	container_move_to(current, destination);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
+
+struct cmd_results *cmd_move(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	int move_amt = 10;
+	if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	struct sway_container *current = config->handler_context.current_container;
+
+	if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
+		char *inv;
+		move_amt = (int)strtol(argv[1], &inv, 10);
+		if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
+			move_amt = 10;
+		}
+	}
+
+	if (strcasecmp(argv[0], "left") == 0) {
+		container_move(current, MOVE_LEFT, move_amt);
+	} else if (strcasecmp(argv[0], "right") == 0) {
+		container_move(current, MOVE_RIGHT, move_amt);
+	} else if (strcasecmp(argv[0], "up") == 0) {
+		container_move(current, MOVE_UP, move_amt);
+	} else if (strcasecmp(argv[0], "down") == 0) {
+		container_move(current, MOVE_DOWN, move_amt);
+	} else if (strcasecmp(argv[0], "container") == 0
+			|| strcasecmp(argv[0], "window") == 0) {
+		return cmd_move_container(current, argc, argv);
+	} else if (strcasecmp(argv[0], "workspace") == 0) {
+		return cmd_move_workspace(current, argc, argv);
+	} else if (strcasecmp(argv[0], "scratchpad") == 0
+			|| (strcasecmp(argv[0], "to") == 0
+				&& strcasecmp(argv[1], "scratchpad") == 0)) {
+		// TODO: scratchpad
+		return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+	} else if (strcasecmp(argv[0], "position") == 0) {
+		// TODO: floating
+		return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+	} else {
+		return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+	}
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 0d706c52..c4265818 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -229,9 +229,12 @@ static void render_output(struct sway_output *output, struct timespec *when,
 	struct sway_seat *seat = input_manager_current_seat(input_manager);
 	struct sway_container *focus =
 		sway_seat_get_focus_inactive(seat, output->swayc);
-	struct sway_container *workspace = (focus->type == C_WORKSPACE ?
-			focus :
-			container_parent(focus, C_WORKSPACE));
+	if (!focus) {
+		// We've never been to this output before
+		focus = output->swayc->children->items[0];
+	}
+	struct sway_container *workspace = focus->type == C_WORKSPACE ?
+			focus : container_parent(focus, C_WORKSPACE);
 
 	struct render_data rdata = {
 		.output = output,
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 99685052..8d22b684 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -97,8 +97,8 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
 	config->handler_context.seat = keyboard->seat_device->sway_seat;
 	struct cmd_results *results = execute_command(binding->command, NULL);
 	if (results->status != CMD_SUCCESS) {
-		wlr_log(L_DEBUG, "could not run command for binding: %s",
-			binding->command);
+		wlr_log(L_DEBUG, "could not run command for binding: %s (%s)",
+			binding->command, results->error);
 	}
 	free_cmd_results(results);
 }
diff --git a/sway/meson.build b/sway/meson.build
index 0cc620ea..38945b4c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -19,6 +19,7 @@ sway_sources = files(
 	'commands/input.c',
 	'commands/layout.c',
 	'commands/mode.c',
+	'commands/move.c',
 	'commands/seat.c',
 	'commands/seat/attach.c',
 	'commands/seat/fallback.c',
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 746dbf1f..21fe3fb0 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -33,6 +33,23 @@ static list_t *get_bfs_queue() {
 	return bfs_queue;
 }
 
+const char *container_type_to_str(enum sway_container_type type) {
+	switch (type) {
+	case C_ROOT:
+		return "C_ROOT";
+	case C_OUTPUT:
+		return "C_OUTPUT";
+	case C_WORKSPACE:
+		return "C_WORKSPACE";
+	case C_CONTAINER:
+		return "C_CONTAINER";
+	case C_VIEW:
+		return "C_VIEW";
+	default:
+		return "C_UNKNOWN";
+	}
+}
+
 static void notify_new_container(struct sway_container *container) {
 	wl_signal_emit(&root_container.sway_root->events.new_container, container);
 	ipc_event_window(container, "new");
@@ -54,6 +71,7 @@ static struct sway_container *container_create(enum sway_container_type type) {
 	}
 
 	wl_signal_init(&c->events.destroy);
+	wl_signal_init(&c->events.reparent);
 
 	return c;
 }
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ce0682dc..a8941a40 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -106,8 +106,10 @@ struct sway_container *container_reap_empty(struct sway_container *container) {
 	if (!sway_assert(container, "reaping null container")) {
 		return NULL;
 	}
-	wlr_log(L_DEBUG, "reaping %p %s", container, container->name);
-	while (container != &root_container && container->children->length == 0) {
+	wlr_log(L_DEBUG, "Reaping %p %s '%s'", container,
+			container_type_to_str(container->type), container->name);
+	while (container->type != C_ROOT && container->type != C_OUTPUT
+			&& container->children->length == 0) {
 		if (container->type == C_WORKSPACE) {
 			if (!workspace_is_visible(container)) {
 				struct sway_container *parent = container->parent;
@@ -138,22 +140,46 @@ struct sway_container *container_remove_child(struct sway_container *child) {
 	return container_reap_empty(parent);
 }
 
-void container_move_to(struct sway_container* container,
-		struct sway_container* destination) {
+void container_move_to(struct sway_container *container,
+		struct sway_container *destination) {
 	if (container == destination
 			|| container_has_anscestor(container, destination)) {
 		return;
 	}
 	struct sway_container *old_parent = container_remove_child(container);
 	container->width = container->height = 0;
-	struct sway_container *new_parent =
-		container_add_sibling(destination, container);
+	struct sway_container *new_parent;
+	if (destination->type == C_VIEW) {
+		new_parent = container_add_sibling(destination, container);
+	} else {
+		new_parent = destination;
+		container_add_child(destination, container);
+	}
+	wl_signal_emit(&container->events.reparent, old_parent);
+	if (container->type == C_WORKSPACE) {
+		struct sway_seat *seat = sway_input_manager_get_default_seat(
+				input_manager);
+		if (old_parent->children->length == 0) {
+			char *ws_name = workspace_next_name(old_parent->name);
+			struct sway_container *ws =
+				container_workspace_create(old_parent, ws_name);
+			free(ws_name);
+			sway_seat_set_focus(seat, ws);
+		}
+		container_sort_workspaces(new_parent);
+		sway_seat_set_focus(seat, new_parent);
+	}
 	if (old_parent) {
 		arrange_windows(old_parent, -1, -1);
 	}
 	arrange_windows(new_parent, -1, -1);
 }
 
+void container_move(struct sway_container *container,
+		enum movement_direction dir, int move_amt) {
+	// TODO
+}
+
 enum sway_container_layout container_get_default_layout(
 		struct sway_container *output) {
 	if (config->default_layout != L_NONE) {
@@ -521,26 +547,6 @@ static struct sway_container *get_swayc_in_direction_under(
 		}
 	}
 
-	if (dir == MOVE_PREV || dir == MOVE_NEXT) {
-		int focused_idx = index_child(container);
-		if (focused_idx == -1) {
-			return NULL;
-		} else {
-			int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
-				parent->children->length;
-			if (desired < 0) {
-				desired += parent->children->length;
-			}
-			return parent->children->items[desired];
-		}
-	}
-
-	// If moving to an adjacent output we need a starting position (since this
-	// output might border to multiple outputs).
-	//struct wlc_point abs_pos;
-	//get_layout_center_position(container, &abs_pos);
-
-
 	// TODO WLR fullscreen
 	/*
 	if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 7248fd00..2331dc2b 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,3 +1,4 @@
+#include <strings.h>
 #include "sway/tree/container.h"
 #include "sway/tree/layout.h"
 #include "sway/output.h"
@@ -37,3 +38,13 @@ struct sway_container *container_output_destroy(struct sway_container *output) {
 	container_destroy(output);
 	return &root_container;
 }
+
+struct sway_container *output_by_name(const char *name) {
+	for (int i = 0; i < root_container.children->length; ++i) {
+		struct sway_container *output = root_container.children->items[i];
+		if (strcasecmp(output->name, name) == 0){
+			return output;
+		}
+	}
+	return NULL;
+}

From e3689dd5a93b4f4ae62637a2b81797335aafdcaf Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
Date: Mon, 2 Apr 2018 11:57:06 -0400
Subject: [PATCH 3/3] Fixes regarding @emersion's feedback

---
 sway/commands/move.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/sway/commands/move.c b/sway/commands/move.c
index 222ef314..ab959b77 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -106,7 +106,9 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
 			// We've never been to this output before
 			focus = destination->children->items[0];
 		}
+		struct sway_container *old_parent = current->parent;
 		container_move_to(current, focus);
+		sway_seat_set_focus(config->handler_context.seat, old_parent);
 		return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 	}
 	return cmd_results_new(CMD_INVALID, "move", expected_syntax);
@@ -149,7 +151,8 @@ struct cmd_results *cmd_move(int argc, char **argv) {
 		char *inv;
 		move_amt = (int)strtol(argv[1], &inv, 10);
 		if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
-			move_amt = 10;
+			return cmd_results_new(CMD_FAILURE, "move",
+					"Invalid distance specified");
 		}
 	}