aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands')
-rw-r--r--sway/commands/border.c13
-rw-r--r--sway/commands/floating.c18
-rw-r--r--sway/commands/focus.c264
-rw-r--r--sway/commands/fullscreen.c18
-rw-r--r--sway/commands/gaps.c35
-rw-r--r--sway/commands/hide_edge_borders.c4
-rw-r--r--sway/commands/kill.c20
-rw-r--r--sway/commands/layout.c186
-rw-r--r--sway/commands/mark.c7
-rw-r--r--sway/commands/move.c873
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/rename.c11
-rw-r--r--sway/commands/resize.c86
-rw-r--r--sway/commands/scratchpad.c42
-rw-r--r--sway/commands/seat/cursor.c4
-rw-r--r--sway/commands/show_marks.c10
-rw-r--r--sway/commands/smart_gaps.c2
-rw-r--r--sway/commands/split.c23
-rw-r--r--sway/commands/sticky.c27
-rw-r--r--sway/commands/swap.c76
-rw-r--r--sway/commands/title_format.c7
-rw-r--r--sway/commands/unmark.c13
-rw-r--r--sway/commands/urgent.c7
-rw-r--r--sway/commands/workspace.c2
25 files changed, 838 insertions, 915 deletions
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 9502c877..95498b2f 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -13,13 +13,12 @@ struct cmd_results *cmd_border(int argc, char **argv) {
13 return error; 13 return error;
14 } 14 }
15 15
16 struct sway_container *container = 16 struct sway_container *container = config->handler_context.container;
17 config->handler_context.current_container; 17 if (!container->view) {
18 if (container->type != C_VIEW) {
19 return cmd_results_new(CMD_INVALID, "border", 18 return cmd_results_new(CMD_INVALID, "border",
20 "Only views can have borders"); 19 "Only views can have borders");
21 } 20 }
22 struct sway_view *view = container->sway_view; 21 struct sway_view *view = container->view;
23 22
24 if (strcmp(argv[0], "none") == 0) { 23 if (strcmp(argv[0], "none") == 0) {
25 view->border = B_NONE; 24 view->border = B_NONE;
@@ -38,11 +37,11 @@ struct cmd_results *cmd_border(int argc, char **argv) {
38 view->border_thickness = atoi(argv[1]); 37 view->border_thickness = atoi(argv[1]);
39 } 38 }
40 39
41 if (container_is_floating(view->swayc)) { 40 if (container_is_floating(view->container)) {
42 container_set_geometry_from_floating_view(view->swayc); 41 container_set_geometry_from_floating_view(view->container);
43 } 42 }
44 43
45 arrange_windows(view->swayc); 44 arrange_container(view->container);
46 45
47 struct sway_seat *seat = input_manager_current_seat(input_manager); 46 struct sway_seat *seat = input_manager_current_seat(input_manager);
48 if (seat->cursor) { 47 if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index 436376e3..d8729094 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -15,24 +15,23 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) { 15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = 18 struct sway_container *container = config->handler_context.container;
19 config->handler_context.current_container; 19 struct sway_workspace *workspace = config->handler_context.workspace;
20 if (container->type == C_WORKSPACE && container->children->length == 0) { 20 if (!container && workspace->tiling->length == 0) {
21 return cmd_results_new(CMD_INVALID, "floating", 21 return cmd_results_new(CMD_INVALID, "floating",
22 "Can't float an empty workspace"); 22 "Can't float an empty workspace");
23 } 23 }
24 if (container->type == C_WORKSPACE) { 24 if (!container) {
25 // Wrap the workspace's children in a container so we can float it 25 // Wrap the workspace's children in a container so we can float it
26 struct sway_container *workspace = container; 26 container = workspace_wrap_children(workspace);
27 container = workspace_wrap_children(container);
28 workspace->layout = L_HORIZ; 27 workspace->layout = L_HORIZ;
29 seat_set_focus(config->handler_context.seat, container); 28 seat_set_focus(config->handler_context.seat, &container->node);
30 } 29 }
31 30
32 // If the container is in a floating split container, 31 // If the container is in a floating split container,
33 // operate on the split container instead of the child. 32 // operate on the split container instead of the child.
34 if (container_is_floating_or_child(container)) { 33 if (container_is_floating_or_child(container)) {
35 while (container->parent->type != C_WORKSPACE) { 34 while (container->parent) {
36 container = container->parent; 35 container = container->parent;
37 } 36 }
38 } 37 }
@@ -51,8 +50,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
51 50
52 container_set_floating(container, wants_floating); 51 container_set_floating(container, wants_floating);
53 52
54 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 53 arrange_workspace(container->workspace);
55 arrange_windows(workspace);
56 54
57 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 55 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
58} 56}
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index f342e524..e31898af 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -34,211 +34,139 @@ static bool parse_movement_direction(const char *name,
34} 34}
35 35
36/** 36/**
37 * Get swayc in the direction of newly entered output. 37 * Get node in the direction of newly entered output.
38 */ 38 */
39static struct sway_container *get_swayc_in_output_direction( 39static struct sway_node *get_node_in_output_direction(
40 struct sway_container *output, enum movement_direction dir, 40 struct sway_output *output, enum movement_direction dir) {
41 struct sway_seat *seat) { 41 struct sway_seat *seat = config->handler_context.seat;
42 if (!output) { 42 struct sway_workspace *ws = output_get_active_workspace(output);
43 return NULL; 43 if (ws->fullscreen) {
44 } 44 return seat_get_focus_inactive(seat, &ws->fullscreen->node);
45
46 struct sway_container *ws = seat_get_focus_inactive(seat, output);
47 if (ws->type != C_WORKSPACE) {
48 ws = container_parent(ws, C_WORKSPACE);
49 }
50
51 if (ws == NULL) {
52 wlr_log(WLR_ERROR, "got an output without a workspace");
53 return NULL;
54 } 45 }
46 struct sway_container *container = NULL;
55 47
56 if (ws->children->length > 0) { 48 if (ws->tiling->length > 0) {
57 switch (dir) { 49 switch (dir) {
58 case MOVE_LEFT: 50 case MOVE_LEFT:
59 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 51 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
60 // get most right child of new output 52 // get most right child of new output
61 return ws->children->items[ws->children->length-1]; 53 container = ws->tiling->items[ws->tiling->length-1];
62 } else { 54 } else {
63 return seat_get_focus_inactive(seat, ws); 55 container = seat_get_focus_inactive_tiling(seat, ws);
64 } 56 }
57 return &container->node;
65 case MOVE_RIGHT: 58 case MOVE_RIGHT:
66 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 59 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
67 // get most left child of new output 60 // get most left child of new output
68 return ws->children->items[0]; 61 container = ws->tiling->items[0];
69 } else { 62 } else {
70 return seat_get_focus_inactive(seat, ws); 63 container = seat_get_focus_inactive_tiling(seat, ws);
71 } 64 }
65 return &container->node;
72 case MOVE_UP: 66 case MOVE_UP:
67 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
68 // get most bottom child of new output
69 container = ws->tiling->items[ws->tiling->length-1];
70 } else {
71 container = seat_get_focus_inactive_tiling(seat, ws);
72 }
73 return &container->node;
73 case MOVE_DOWN: { 74 case MOVE_DOWN: {
74 struct sway_container *focused = 75 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
75 seat_get_focus_inactive(seat, ws); 76 // get most top child of new output
76 if (focused && focused->parent) { 77 container = ws->tiling->items[0];
77 struct sway_container *parent = focused->parent; 78 } else {
78 if (parent->layout == L_VERT) { 79 container = seat_get_focus_inactive_tiling(seat, ws);
79 if (dir == MOVE_UP) {
80 // get child furthest down on new output
81 int idx = parent->children->length - 1;
82 return parent->children->items[idx];
83 } else if (dir == MOVE_DOWN) {
84 // get child furthest up on new output
85 return parent->children->items[0];
86 }
87 }
88 return focused;
89 } 80 }
90 break; 81 return &container->node;
91 } 82 }
92 default: 83 default:
93 break; 84 break;
94 } 85 }
95 } 86 }
96 87
97 return ws; 88 return &ws->node;
98} 89}
99 90
100static struct sway_container *container_get_in_direction( 91static struct sway_node *node_get_in_direction(struct sway_container *container,
101 struct sway_container *container, struct sway_seat *seat, 92 struct sway_seat *seat, enum movement_direction dir) {
102 enum movement_direction dir) {
103 struct sway_container *parent = container->parent;
104
105 if (dir == MOVE_CHILD) { 93 if (dir == MOVE_CHILD) {
106 return seat_get_focus_inactive(seat, container); 94 return seat_get_active_child(seat, &container->node);
107 } 95 }
108 if (container->is_fullscreen) { 96 if (container->is_fullscreen) {
109 if (dir == MOVE_PARENT) { 97 if (dir == MOVE_PARENT) {
110 return NULL; 98 return NULL;
111 } 99 }
112 container = container_parent(container, C_OUTPUT); 100 // Fullscreen container with a direction - go straight to outputs
113 parent = container->parent; 101 struct sway_output *output = container->workspace->output;
114 } else { 102 struct sway_output *new_output = output_get_in_direction(output, dir);
115 if (dir == MOVE_PARENT) { 103 return get_node_in_output_direction(new_output, dir);
116 if (parent->type == C_OUTPUT || container_is_floating(container)) { 104 }
117 return NULL; 105 if (dir == MOVE_PARENT) {
118 } else { 106 return node_get_parent(&container->node);
119 return parent;
120 }
121 }
122 } 107 }
123 108
124 struct sway_container *wrap_candidate = NULL; 109 struct sway_container *wrap_candidate = NULL;
125 while (true) { 110 struct sway_container *current = container;
111 while (current) {
126 bool can_move = false; 112 bool can_move = false;
127 int desired; 113 int desired;
128 int idx = list_find(container->parent->children, container); 114 int idx = container_sibling_index(current);
129 if (idx == -1) { 115 enum sway_container_layout parent_layout =
130 return NULL; 116 container_parent_layout(current);
131 } 117 list_t *siblings = container_get_siblings(current);
132 if (parent->type == C_ROOT) {
133 enum wlr_direction wlr_dir = 0;
134 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
135 "got invalid direction: %d", dir)) {
136 return NULL;
137 }
138 int lx = container->x + container->width / 2;
139 int ly = container->y + container->height / 2;
140 struct wlr_output_layout *layout =
141 root_container.sway_root->output_layout;
142 struct wlr_output *wlr_adjacent =
143 wlr_output_layout_adjacent_output(layout, wlr_dir,
144 container->sway_output->wlr_output, lx, ly);
145 struct sway_container *adjacent =
146 output_from_wlr_output(wlr_adjacent);
147 118
148 if (!adjacent || adjacent == container) { 119 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
149 if (!wrap_candidate) { 120 if (parent_layout == L_HORIZ || parent_layout == L_TABBED) {
150 return NULL; 121 can_move = true;
151 } 122 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
152 return seat_get_focus_inactive_view(seat, wrap_candidate);
153 }
154 struct sway_container *next =
155 get_swayc_in_output_direction(adjacent, dir, seat);
156 if (next == NULL) {
157 return NULL;
158 }
159 struct sway_container *next_workspace = next;
160 if (next_workspace->type != C_WORKSPACE) {
161 next_workspace = container_parent(next_workspace, C_WORKSPACE);
162 }
163 sway_assert(next_workspace, "Next container has no workspace");
164 if (next_workspace->sway_workspace->fullscreen) {
165 return seat_get_focus_inactive(seat,
166 next_workspace->sway_workspace->fullscreen);
167 }
168 if (next->children && next->children->length) {
169 // TODO consider floating children as well
170 return seat_get_focus_inactive_view(seat, next);
171 } else {
172 return next;
173 } 123 }
174 } else { 124 } else {
175 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 125 if (parent_layout == L_VERT || parent_layout == L_STACKED) {
176 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { 126 can_move = true;
177 can_move = true; 127 desired = idx + (dir == MOVE_UP ? -1 : 1);
178 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
179 }
180 } else {
181 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
182 can_move = true;
183 desired = idx + (dir == MOVE_UP ? -1 : 1);
184 }
185 } 128 }
186 } 129 }
187 130
188 if (can_move) { 131 if (can_move) {
189 // TODO handle floating 132 if (desired < 0 || desired >= siblings->length) {
190 if (desired < 0 || desired >= parent->children->length) {
191 can_move = false; 133 can_move = false;
192 int len = parent->children->length; 134 int len = siblings->length;
193 if (config->focus_wrapping != WRAP_NO && !wrap_candidate 135 if (config->focus_wrapping != WRAP_NO && !wrap_candidate
194 && len > 1) { 136 && len > 1) {
195 if (desired < 0) { 137 if (desired < 0) {
196 wrap_candidate = parent->children->items[len-1]; 138 wrap_candidate = siblings->items[len-1];
197 } else { 139 } else {
198 wrap_candidate = parent->children->items[0]; 140 wrap_candidate = siblings->items[0];
199 } 141 }
200 if (config->focus_wrapping == WRAP_FORCE) { 142 if (config->focus_wrapping == WRAP_FORCE) {
201 return seat_get_focus_inactive_view(seat, 143 struct sway_container *c = seat_get_focus_inactive_view(
202 wrap_candidate); 144 seat, &wrap_candidate->node);
145 return &c->node;
203 } 146 }
204 } 147 }
205 } else { 148 } else {
206 struct sway_container *desired_con = 149 struct sway_container *desired_con = siblings->items[desired];
207 parent->children->items[desired]; 150 struct sway_container *c = seat_get_focus_inactive_view(
208 wlr_log(WLR_DEBUG, 151 seat, &desired_con->node);
209 "cont %d-%p dir %i sibling %d: %p", idx, 152 return &c->node;
210 container, dir, desired, desired_con);
211 return seat_get_focus_inactive_view(seat, desired_con);
212 } 153 }
213 } 154 }
214 155
215 if (!can_move) { 156 current = current->parent;
216 container = parent;
217 parent = parent->parent;
218 if (!parent) {
219 // wrapping is the last chance
220 if (!wrap_candidate) {
221 return NULL;
222 }
223 return seat_get_focus_inactive_view(seat, wrap_candidate);
224 }
225 }
226 } 157 }
227}
228 158
229static struct cmd_results *focus_mode(struct sway_container *con, 159 // Check a different output
230 struct sway_seat *seat, bool floating) { 160 struct sway_output *output = container->workspace->output;
231 struct sway_container *ws = con->type == C_WORKSPACE ? 161 struct sway_output *new_output = output_get_in_direction(output, dir);
232 con : container_parent(con, C_WORKSPACE); 162 if (new_output) {
233 163 return get_node_in_output_direction(new_output, dir);
234 // If the container is in a floating split container,
235 // operate on the split container instead of the child.
236 if (container_is_floating_or_child(con)) {
237 while (con->parent->type != C_WORKSPACE) {
238 con = con->parent;
239 }
240 } 164 }
165 return NULL;
166}
241 167
168static struct cmd_results *focus_mode(struct sway_workspace *ws,
169 struct sway_seat *seat, bool floating) {
242 struct sway_container *new_focus = NULL; 170 struct sway_container *new_focus = NULL;
243 if (floating) { 171 if (floating) {
244 new_focus = seat_get_focus_inactive_floating(seat, ws); 172 new_focus = seat_get_focus_inactive_floating(seat, ws);
@@ -246,7 +174,7 @@ static struct cmd_results *focus_mode(struct sway_container *con,
246 new_focus = seat_get_focus_inactive_tiling(seat, ws); 174 new_focus = seat_get_focus_inactive_tiling(seat, ws);
247 } 175 }
248 if (new_focus) { 176 if (new_focus) {
249 seat_set_focus(seat, new_focus); 177 seat_set_focus(seat, &new_focus->node);
250 } else { 178 } else {
251 return cmd_results_new(CMD_FAILURE, "focus", 179 return cmd_results_new(CMD_FAILURE, "focus",
252 "Failed to find a %s container in workspace", 180 "Failed to find a %s container in workspace",
@@ -255,14 +183,14 @@ static struct cmd_results *focus_mode(struct sway_container *con,
255 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 183 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
256} 184}
257 185
258static struct cmd_results *focus_output(struct sway_container *con, 186static struct cmd_results *focus_output(struct sway_seat *seat,
259 struct sway_seat *seat, int argc, char **argv) { 187 int argc, char **argv) {
260 if (!argc) { 188 if (!argc) {
261 return cmd_results_new(CMD_INVALID, "focus", 189 return cmd_results_new(CMD_INVALID, "focus",
262 "Expected 'focus output <direction|name>'"); 190 "Expected 'focus output <direction|name>'");
263 } 191 }
264 char *identifier = join_args(argv, argc); 192 char *identifier = join_args(argv, argc);
265 struct sway_container *output = output_by_name(identifier); 193 struct sway_output *output = output_by_name(identifier);
266 194
267 if (!output) { 195 if (!output) {
268 enum movement_direction direction; 196 enum movement_direction direction;
@@ -272,14 +200,13 @@ static struct cmd_results *focus_output(struct sway_container *con,
272 return cmd_results_new(CMD_INVALID, "focus", 200 return cmd_results_new(CMD_INVALID, "focus",
273 "There is no output with that name"); 201 "There is no output with that name");
274 } 202 }
275 struct sway_container *focus = seat_get_focus(seat); 203 struct sway_workspace *ws = seat_get_focused_workspace(seat);
276 focus = container_parent(focus, C_OUTPUT); 204 output = output_get_in_direction(ws->output, direction);
277 output = container_get_in_direction(focus, seat, direction);
278 } 205 }
279 206
280 free(identifier); 207 free(identifier);
281 if (output) { 208 if (output) {
282 seat_set_focus(seat, seat_get_focus_inactive(seat, output)); 209 seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));
283 } 210 }
284 211
285 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 212 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -289,29 +216,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
289 if (config->reading || !config->active) { 216 if (config->reading || !config->active) {
290 return cmd_results_new(CMD_DEFER, NULL, NULL); 217 return cmd_results_new(CMD_DEFER, NULL, NULL);
291 } 218 }
292 struct sway_container *con = config->handler_context.current_container; 219 struct sway_node *node = config->handler_context.node;
220 struct sway_container *container = config->handler_context.container;
221 struct sway_workspace *workspace = config->handler_context.workspace;
293 struct sway_seat *seat = config->handler_context.seat; 222 struct sway_seat *seat = config->handler_context.seat;
294 if (con->type < C_WORKSPACE) { 223 if (node->type < N_WORKSPACE) {
295 return cmd_results_new(CMD_FAILURE, "focus", 224 return cmd_results_new(CMD_FAILURE, "focus",
296 "Command 'focus' cannot be used above the workspace level"); 225 "Command 'focus' cannot be used above the workspace level");
297 } 226 }
298 227
299 if (argc == 0) { 228 if (argc == 0) {
300 seat_set_focus(seat, con); 229 seat_set_focus(seat, node);
301 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 230 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
302 } 231 }
303 232
304 if (strcmp(argv[0], "floating") == 0) { 233 if (strcmp(argv[0], "floating") == 0) {
305 return focus_mode(con, seat, true); 234 return focus_mode(workspace, seat, true);
306 } else if (strcmp(argv[0], "tiling") == 0) { 235 } else if (strcmp(argv[0], "tiling") == 0) {
307 return focus_mode(con, seat, false); 236 return focus_mode(workspace, seat, false);
308 } else if (strcmp(argv[0], "mode_toggle") == 0) { 237 } else if (strcmp(argv[0], "mode_toggle") == 0) {
309 return focus_mode(con, seat, !container_is_floating_or_child(con)); 238 bool floating = container && container_is_floating_or_child(container);
239 return focus_mode(workspace, seat, !floating);
310 } 240 }
311 241
312 if (strcmp(argv[0], "output") == 0) { 242 if (strcmp(argv[0], "output") == 0) {
313 argc--; argv++; 243 argc--; argv++;
314 return focus_output(con, seat, argc, argv); 244 return focus_output(seat, argc, argv);
315 } 245 }
316 246
317 enum movement_direction direction = 0; 247 enum movement_direction direction = 0;
@@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
321 "or 'focus output <direction|name>'"); 251 "or 'focus output <direction|name>'");
322 } 252 }
323 253
324 struct sway_container *next_focus = container_get_in_direction( 254 if (node->type == N_WORKSPACE) {
325 con, seat, direction); 255 // A workspace is focused, so just jump to the next output
256 struct sway_output *new_output =
257 output_get_in_direction(workspace->output, direction);
258 struct sway_node *node =
259 get_node_in_output_direction(new_output, direction);
260 seat_set_focus(seat, node);
261 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
262 }
263
264 struct sway_node *next_focus =
265 node_get_in_direction(container, seat, direction);
326 if (next_focus) { 266 if (next_focus) {
327 seat_set_focus(seat, next_focus); 267 seat_set_focus(seat, next_focus);
328 } 268 }
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index ac65dffb..3bbe00c5 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -12,18 +12,18 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
12 if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) { 12 if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) {
13 return error; 13 return error;
14 } 14 }
15 struct sway_container *container = 15 struct sway_node *node = config->handler_context.node;
16 config->handler_context.current_container; 16 struct sway_container *container = config->handler_context.container;
17 if (container->type == C_WORKSPACE && container->children->length == 0) { 17 struct sway_workspace *workspace = config->handler_context.workspace;
18 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
18 return cmd_results_new(CMD_INVALID, "fullscreen", 19 return cmd_results_new(CMD_INVALID, "fullscreen",
19 "Can't fullscreen an empty workspace"); 20 "Can't fullscreen an empty workspace");
20 } 21 }
21 if (container->type == C_WORKSPACE) { 22 if (node->type == N_WORKSPACE) {
22 // Wrap the workspace's children in a container so we can fullscreen it 23 // Wrap the workspace's children in a container so we can fullscreen it
23 struct sway_container *workspace = container; 24 container = workspace_wrap_children(workspace);
24 container = workspace_wrap_children(container);
25 workspace->layout = L_HORIZ; 25 workspace->layout = L_HORIZ;
26 seat_set_focus(config->handler_context.seat, container); 26 seat_set_focus(config->handler_context.seat, &container->node);
27 } 27 }
28 bool enable = !container->is_fullscreen; 28 bool enable = !container->is_fullscreen;
29 29
@@ -32,9 +32,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
32 } 32 }
33 33
34 container_set_fullscreen(container, enable); 34 container_set_fullscreen(container, enable);
35 35 arrange_workspace(workspace);
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_windows(workspace->parent);
38 36
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 38}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 3906eb70..d676e475 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -2,6 +2,7 @@
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
5#include "sway/tree/workspace.h"
5#include "log.h" 6#include "log.h"
6#include "stringop.h" 7#include "stringop.h"
7#include <math.h> 8#include <math.h>
@@ -43,7 +44,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
43 return cmd_results_new(CMD_INVALID, "gaps", 44 return cmd_results_new(CMD_INVALID, "gaps",
44 "gaps edge_gaps on|off|toggle"); 45 "gaps edge_gaps on|off|toggle");
45 } 46 }
46 arrange_windows(&root_container); 47 arrange_root();
47 } else { 48 } else {
48 int amount_idx = 0; // the current index in argv 49 int amount_idx = 0; // the current index in argv
49 enum gaps_op op = GAPS_OP_SET; 50 enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +125,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
124 if (amount_idx == 0) { // gaps <amount> 125 if (amount_idx == 0) { // gaps <amount>
125 config->gaps_inner = val; 126 config->gaps_inner = val;
126 config->gaps_outer = val; 127 config->gaps_outer = val;
127 arrange_windows(&root_container); 128 arrange_root();
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 129 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 130 }
130 // Other variants. The middle-length variant (gaps inner|outer <amount>) 131 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,21 +156,27 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
155 } else { 156 } else {
156 config->gaps_outer = total; 157 config->gaps_outer = total;
157 } 158 }
158 arrange_windows(&root_container); 159 arrange_root();
159 } else { 160 } else {
160 struct sway_container *c = 161 if (scope == GAPS_SCOPE_WORKSPACE) {
161 config->handler_context.current_container; 162 struct sway_workspace *ws = config->handler_context.workspace;
162 if (scope == GAPS_SCOPE_WORKSPACE && c->type != C_WORKSPACE) { 163 ws->has_gaps = true;
163 c = container_parent(c, C_WORKSPACE); 164 if (inner) {
164 } 165 ws->gaps_inner = total;
165 c->has_gaps = true; 166 } else {
166 if (inner) { 167 ws->gaps_outer = total;
167 c->gaps_inner = total; 168 }
169 arrange_workspace(ws);
168 } else { 170 } else {
169 c->gaps_outer = total; 171 struct sway_container *c = config->handler_context.container;
172 c->has_gaps = true;
173 if (inner) {
174 c->gaps_inner = total;
175 } else {
176 c->gaps_outer = total;
177 }
178 arrange_workspace(c->workspace);
170 } 179 }
171
172 arrange_windows(c->parent ? c->parent : &root_container);
173 } 180 }
174 } 181 }
175 182
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index e494f6aa..0a5c7f28 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -5,8 +5,8 @@
5#include "sway/tree/view.h" 5#include "sway/tree/view.h"
6 6
7static void _configure_view(struct sway_container *con, void *data) { 7static void _configure_view(struct sway_container *con, void *data) {
8 if (con->type == C_VIEW) { 8 if (con->view) {
9 view_autoconfigure(con->sway_view); 9 view_autoconfigure(con->view);
10 } 10 }
11} 11}
12 12
diff --git a/sway/commands/kill.c b/sway/commands/kill.c
index f3fa52f1..85ca0f33 100644
--- a/sway/commands/kill.c
+++ b/sway/commands/kill.c
@@ -2,15 +2,27 @@
2#include "log.h" 2#include "log.h"
3#include "sway/input/input-manager.h" 3#include "sway/input/input-manager.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h"
7#include "sway/tree/workspace.h"
7#include "sway/commands.h" 8#include "sway/commands.h"
8 9
10static void close_container_iterator(struct sway_container *con, void *data) {
11 if (con->view) {
12 view_close(con->view);
13 }
14}
15
9struct cmd_results *cmd_kill(int argc, char **argv) { 16struct cmd_results *cmd_kill(int argc, char **argv) {
10 struct sway_container *con = 17 struct sway_container *con = config->handler_context.container;
11 config->handler_context.current_container; 18 struct sway_workspace *ws = config->handler_context.workspace;
12 19
13 container_close(con); 20 if (con) {
21 close_container_iterator(con, NULL);
22 container_for_each_child(con, close_container_iterator, NULL);
23 } else {
24 workspace_for_each_container(ws, close_container_iterator, NULL);
25 }
14 26
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
16} 28}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index a06832de..8fa1ce98 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -4,21 +4,20 @@
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/tree/arrange.h" 5#include "sway/tree/arrange.h"
6#include "sway/tree/container.h" 6#include "sway/tree/container.h"
7#include "sway/tree/workspace.h"
7#include "log.h" 8#include "log.h"
8 9
9static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { 10static enum sway_container_layout parse_layout_string(char *s) {
10 if (strcasecmp(s, "splith") == 0) { 11 if (strcasecmp(s, "splith") == 0) {
11 *ptr = L_HORIZ; 12 return L_HORIZ;
12 } else if (strcasecmp(s, "splitv") == 0) { 13 } else if (strcasecmp(s, "splitv") == 0) {
13 *ptr = L_VERT; 14 return L_VERT;
14 } else if (strcasecmp(s, "tabbed") == 0) { 15 } else if (strcasecmp(s, "tabbed") == 0) {
15 *ptr = L_TABBED; 16 return L_TABBED;
16 } else if (strcasecmp(s, "stacking") == 0) { 17 } else if (strcasecmp(s, "stacking") == 0) {
17 *ptr = L_STACKED; 18 return L_STACKED;
18 } else {
19 return false;
20 } 19 }
21 return true; 20 return L_NONE;
22} 21}
23 22
24static const char* expected_syntax = 23static const char* expected_syntax =
@@ -26,84 +25,129 @@ static const char* expected_syntax =
26 "'layout toggle [split|all]' or " 25 "'layout toggle [split|all]' or "
27 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; 26 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
28 27
28static enum sway_container_layout get_layout_toggle(int argc, char **argv,
29 enum sway_container_layout layout,
30 enum sway_container_layout prev_split_layout) {
31 // "layout toggle"
32 if (argc == 0) {
33 return layout == L_HORIZ ? L_VERT : L_HORIZ;
34 }
35
36 if (argc == 2) {
37 // "layout toggle split" (same as "layout toggle")
38 if (strcasecmp(argv[1], "split") == 0) {
39 return layout == L_HORIZ ? L_VERT : L_HORIZ;
40 }
41 // "layout toggle all"
42 if (strcasecmp(argv[1], "all") == 0) {
43 return layout == L_HORIZ ? L_VERT :
44 layout == L_VERT ? L_STACKED :
45 layout == L_STACKED ? L_TABBED : L_HORIZ;
46 }
47 return L_NONE;
48 }
49
50 enum sway_container_layout parsed;
51 int curr = 1;
52 for (; curr < argc; curr++) {
53 parsed = parse_layout_string(argv[curr]);
54 if (parsed == layout || (strcmp(argv[curr], "split") == 0 &&
55 (layout == L_VERT || layout == L_HORIZ))) {
56 break;
57 }
58 }
59 for (int i = curr + 1; i != curr; ++i) {
60 // cycle round to find next valid layout
61 if (i >= argc) {
62 i = 1;
63 }
64 parsed = parse_layout_string(argv[i]);
65 if (parsed != L_NONE) {
66 return parsed;
67 }
68 if (strcmp(argv[i], "split") == 0) {
69 return layout == L_HORIZ ? L_VERT :
70 layout == L_VERT ? L_HORIZ : prev_split_layout;
71 }
72 // invalid layout strings are silently ignored
73 }
74 return L_NONE;
75}
76
77static enum sway_container_layout get_layout(int argc, char **argv,
78 enum sway_container_layout layout,
79 enum sway_container_layout prev_split_layout) {
80 // Check if assigned directly
81 enum sway_container_layout parsed = parse_layout_string(argv[0]);
82 if (parsed != L_NONE) {
83 return parsed;
84 }
85
86 if (strcasecmp(argv[0], "default") == 0) {
87 return prev_split_layout;
88 }
89
90 if (strcasecmp(argv[0], "toggle") == 0) {
91 argc--; argv++;
92 return get_layout_toggle(argc, argv, layout, prev_split_layout);
93 }
94
95 return L_NONE;
96}
97
29struct cmd_results *cmd_layout(int argc, char **argv) { 98struct cmd_results *cmd_layout(int argc, char **argv) {
30 struct cmd_results *error = NULL; 99 struct cmd_results *error = NULL;
31 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { 100 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
32 return error; 101 return error;
33 } 102 }
34 struct sway_container *parent = config->handler_context.current_container; 103 struct sway_container *container = config->handler_context.container;
104 struct sway_workspace *workspace = config->handler_context.workspace;
35 105
36 if (container_is_floating(parent)) { 106 if (container && container_is_floating(container)) {
37 return cmd_results_new(CMD_FAILURE, "layout", 107 return cmd_results_new(CMD_FAILURE, "layout",
38 "Unable to change layout of floating windows"); 108 "Unable to change layout of floating windows");
39 } 109 }
40 110
41 while (parent->type == C_VIEW) { 111 // Typically we change the layout of the current container, but if the
42 parent = parent->parent; 112 // current container is a view (it usually is) then we'll change the layout
113 // of the parent instead, as it doesn't make sense for views to have layout.
114 if (container && container->view) {
115 container = container->parent;
43 } 116 }
44 117
45 enum sway_container_layout prev = parent->layout; 118 // We could be working with a container OR a workspace. These are different
46 bool assigned_directly = parse_layout_string(argv[0], &parent->layout); 119 // structures, so we set up pointers to they layouts so we can refer them in
47 if (!assigned_directly) { 120 // an abstract way.
48 if (strcasecmp(argv[0], "default") == 0) { 121 enum sway_container_layout new_layout = L_NONE;
49 parent->layout = parent->prev_split_layout; 122 enum sway_container_layout old_layout = L_NONE;
50 } else if (strcasecmp(argv[0], "toggle") == 0) { 123 if (container) {
51 if (argc == 1) { 124 old_layout = container->layout;
52 parent->layout = 125 new_layout = get_layout(argc, argv,
53 parent->layout == L_STACKED ? L_TABBED : 126 container->layout, container->prev_split_layout);
54 parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; 127 } else {
55 } else if (argc == 2) { 128 old_layout = workspace->layout;
56 if (strcasecmp(argv[1], "all") == 0) { 129 new_layout = get_layout(argc, argv,
57 parent->layout = 130 workspace->layout, workspace->prev_split_layout);
58 parent->layout == L_HORIZ ? L_VERT :
59 parent->layout == L_VERT ? L_STACKED :
60 parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
61 } else if (strcasecmp(argv[1], "split") == 0) {
62 parent->layout =
63 parent->layout == L_HORIZ ? L_VERT :
64 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
65 } else {
66 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
67 }
68 } else {
69 enum sway_container_layout parsed_layout;
70 int curr = 1;
71 for (; curr < argc; curr++) {
72 bool valid = parse_layout_string(argv[curr], &parsed_layout);
73 if ((valid && parsed_layout == parent->layout) ||
74 (strcmp(argv[curr], "split") == 0 &&
75 (parent->layout == L_VERT || parent->layout == L_HORIZ))) {
76 break;
77 }
78 }
79 for (int i = curr + 1; i != curr; ++i) {
80 // cycle round to find next valid layout
81 if (i >= argc) {
82 i = 1;
83 }
84 if (parse_layout_string(argv[i], &parent->layout)) {
85 break;
86 } else if (strcmp(argv[i], "split") == 0) {
87 parent->layout =
88 parent->layout == L_HORIZ ? L_VERT :
89 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
90 break;
91 } // invalid layout strings are silently ignored
92 }
93 }
94 } else {
95 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
96 }
97 } 131 }
98 if (parent->layout == L_NONE) { 132 if (new_layout == L_NONE) {
99 parent->layout = container_get_default_layout(parent); 133 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
100 } 134 }
101 if (prev != parent->layout) { 135 if (new_layout != old_layout) {
102 if (prev != L_TABBED && prev != L_STACKED) { 136 if (container) {
103 parent->prev_split_layout = prev; 137 if (old_layout != L_TABBED && old_layout != L_STACKED) {
138 container->prev_split_layout = old_layout;
139 }
140 container->layout = new_layout;
141 container_update_representation(container);
142 arrange_container(container);
143 } else {
144 if (old_layout != L_TABBED && old_layout != L_STACKED) {
145 workspace->prev_split_layout = old_layout;
146 }
147 workspace->layout = new_layout;
148 workspace_update_representation(workspace);
149 arrange_workspace(workspace);
104 } 150 }
105 container_notify_subtree_changed(parent);
106 arrange_windows(parent->parent);
107 } 151 }
108 152
109 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 153 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index 9ea8c301..fb95a7d0 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -18,13 +18,12 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
18 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) { 18 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
19 return error; 19 return error;
20 } 20 }
21 struct sway_container *container = 21 struct sway_container *container = config->handler_context.container;
22 config->handler_context.current_container; 22 if (!container->view) {
23 if (container->type != C_VIEW) {
24 return cmd_results_new(CMD_INVALID, "mark", 23 return cmd_results_new(CMD_INVALID, "mark",
25 "Only views can have marks"); 24 "Only views can have marks");
26 } 25 }
27 struct sway_view *view = container->sway_view; 26 struct sway_view *view = container->view;
28 27
29 bool add = false, toggle = false; 28 bool add = false, toggle = false;
30 while (argc > 0 && strncmp(*argv, "--", 2) == 0) { 29 while (argc > 0 && strncmp(*argv, "--", 2) == 0) {
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 4426f24e..1b2e830c 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -40,7 +40,7 @@ enum wlr_direction opposite_direction(enum wlr_direction d) {
40 } 40 }
41} 41}
42 42
43static struct sway_container *output_in_direction(const char *direction_string, 43static struct sway_output *output_in_direction(const char *direction_string,
44 struct wlr_output *reference, int ref_lx, int ref_ly) { 44 struct wlr_output *reference, int ref_lx, int ref_ly) {
45 struct { 45 struct {
46 char *name; 46 char *name;
@@ -63,447 +63,367 @@ static struct sway_container *output_in_direction(const char *direction_string,
63 63
64 if (direction) { 64 if (direction) {
65 struct wlr_output *target = wlr_output_layout_adjacent_output( 65 struct wlr_output *target = wlr_output_layout_adjacent_output(
66 root_container.sway_root->output_layout, 66 root->output_layout, direction, reference, ref_lx, ref_ly);
67 direction, reference, ref_lx, ref_ly);
68 67
69 if (!target) { 68 if (!target) {
70 target = wlr_output_layout_farthest_output( 69 target = wlr_output_layout_farthest_output(
71 root_container.sway_root->output_layout, 70 root->output_layout, opposite_direction(direction),
72 opposite_direction(direction), reference, ref_lx, ref_ly); 71 reference, ref_lx, ref_ly);
73 } 72 }
74 73
75 if (target) { 74 if (target) {
76 struct sway_output *sway_output = target->data; 75 return target->data;
77 return sway_output->swayc;
78 } 76 }
79 } 77 }
80 78
81 return output_by_name(direction_string); 79 return output_by_name(direction_string);
82} 80}
83 81
84static void container_move_to(struct sway_container *container, 82static bool is_parallel(enum sway_container_layout layout,
85 struct sway_container *destination) { 83 enum movement_direction dir) {
86 if (!sway_assert(container->type == C_CONTAINER || 84 switch (layout) {
87 container->type == C_VIEW, "Expected a container or view")) { 85 case L_TABBED:
88 return; 86 case L_HORIZ:
87 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
88 case L_STACKED:
89 case L_VERT:
90 return dir == MOVE_UP || dir == MOVE_DOWN;
91 default:
92 return false;
89 } 93 }
90 if (container == destination 94}
91 || container_has_ancestor(container, destination)) { 95
96/**
97 * Ensures all seats focus the fullscreen container if needed.
98 */
99static void workspace_focus_fullscreen(struct sway_workspace *workspace) {
100 if (!workspace->fullscreen) {
92 return; 101 return;
93 } 102 }
94 struct sway_container *old_parent = NULL; 103 struct sway_seat *seat;
95 struct sway_container *new_parent = NULL; 104 struct sway_workspace *focus_ws;
96 if (container_is_floating(container)) { 105 wl_list_for_each(seat, &input_manager->seats, link) {
97 // Resolve destination into a workspace 106 focus_ws = seat_get_focused_workspace(seat);
98 struct sway_container *new_ws = NULL; 107 if (focus_ws == workspace) {
99 if (destination->type == C_OUTPUT) { 108 struct sway_node *new_focus =
100 new_ws = output_get_active_workspace(destination->sway_output); 109 seat_get_focus_inactive(seat, &workspace->fullscreen->node);
101 } else if (destination->type == C_WORKSPACE) { 110 seat_set_focus(seat, new_focus);
102 new_ws = destination;
103 } else {
104 new_ws = container_parent(destination, C_WORKSPACE);
105 } 111 }
106 if (!new_ws) { 112 }
107 // This can happen if the user has run "move container to mark foo", 113}
108 // where mark foo is on a hidden scratchpad container. 114
109 return; 115static void container_move_to_container_from_direction(
116 struct sway_container *container, struct sway_container *destination,
117 enum movement_direction move_dir) {
118 if (destination->view) {
119 if (destination->parent == container->parent) {
120 wlr_log(WLR_DEBUG, "Swapping siblings");
121 list_t *siblings = container_get_siblings(container);
122 int container_index = list_find(siblings, container);
123 int destination_index = list_find(siblings, destination);
124 list_swap(siblings, container_index, destination_index);
125 } else {
126 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
127 int offset = move_dir == MOVE_LEFT || move_dir == MOVE_UP;
128 int index = container_sibling_index(destination) + offset;
129 if (destination->parent) {
130 container_insert_child(destination->parent, container, index);
131 } else {
132 workspace_insert_tiling(destination->workspace,
133 container, index);
134 }
135 container->width = container->height = 0;
110 } 136 }
111 struct sway_container *old_output = 137 return;
112 container_parent(container, C_OUTPUT); 138 }
113 old_parent = container_remove_child(container); 139
114 workspace_add_floating(new_ws, container); 140 if (is_parallel(destination->layout, move_dir)) {
115 container_handle_fullscreen_reparent(container, old_parent); 141 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
142 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
143 0 : destination->children->length;
144 container_insert_child(destination, container, index);
145 container->width = container->height = 0;
146 return;
147 }
148
149 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
150 struct sway_node *focus_inactive = seat_get_active_child(
151 config->handler_context.seat, &destination->node);
152 if (!focus_inactive || focus_inactive == &destination->node) {
153 // The container has no children
154 container_add_child(destination, container);
155 return;
156 }
157
158 // Try again but with the child
159 container_move_to_container_from_direction(container,
160 focus_inactive->sway_container, move_dir);
161}
162
163static void container_move_to_workspace_from_direction(
164 struct sway_container *container, struct sway_workspace *workspace,
165 enum movement_direction move_dir) {
166 if (is_parallel(workspace->layout, move_dir)) {
167 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
168 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
169 0 : workspace->tiling->length;
170 workspace_insert_tiling(workspace, container, index);
171 return;
172 }
173
174 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
175 struct sway_container *focus_inactive = seat_get_focus_inactive_tiling(
176 config->handler_context.seat, workspace);
177 if (!focus_inactive) {
178 // The workspace has no tiling children
179 workspace_add_tiling(workspace, container);
180 return;
181 }
182 while (focus_inactive->parent) {
183 focus_inactive = focus_inactive->parent;
184 }
185 container_move_to_container_from_direction(container, focus_inactive,
186 move_dir);
187}
188
189static void container_move_to_workspace(struct sway_container *container,
190 struct sway_workspace *workspace) {
191 if (container->workspace == workspace) {
192 return;
193 }
194 struct sway_workspace *old_workspace = container->workspace;
195 if (container_is_floating(container)) {
196 struct sway_output *old_output = container->workspace->output;
197 container_detach(container);
198 workspace_add_floating(workspace, container);
199 container_handle_fullscreen_reparent(container);
116 // If changing output, center it within the workspace 200 // If changing output, center it within the workspace
117 if (old_output != new_ws->parent && !container->is_fullscreen) { 201 if (old_output != workspace->output && !container->is_fullscreen) {
118 container_floating_move_to_center(container); 202 container_floating_move_to_center(container);
119 } 203 }
120 } else { 204 } else {
121 old_parent = container_remove_child(container); 205 container_detach(container);
122 container->width = container->height = 0; 206 container->width = container->height = 0;
123 container->saved_width = container->saved_height = 0; 207 container->saved_width = container->saved_height = 0;
124 208 workspace_add_tiling(workspace, container);
125 if (destination->type == C_VIEW) { 209 container_update_representation(container);
126 new_parent = container_add_sibling(destination, container);
127 } else {
128 new_parent = destination;
129 container_add_child(destination, container);
130 }
131 } 210 }
132 211 if (container->view) {
133 if (container->type == C_VIEW) {
134 ipc_event_window(container, "move"); 212 ipc_event_window(container, "move");
135 } 213 }
136 container_notify_subtree_changed(old_parent); 214 workspace_detect_urgent(old_workspace);
137 container_notify_subtree_changed(new_parent); 215 workspace_detect_urgent(workspace);
138 216 workspace_focus_fullscreen(workspace);
139 // If view was moved to a fullscreen workspace, refocus the fullscreen view 217}
140 struct sway_container *new_workspace = container; 218
141 if (new_workspace->type != C_WORKSPACE) { 219static void container_move_to_container(struct sway_container *container,
142 new_workspace = container_parent(new_workspace, C_WORKSPACE); 220 struct sway_container *destination) {
143 } 221 if (container == destination
144 if (new_workspace->sway_workspace->fullscreen) { 222 || container_has_ancestor(container, destination)
145 struct sway_seat *seat; 223 || container_has_ancestor(destination, container)) {
146 struct sway_container *focus, *focus_ws; 224 return;
147 wl_list_for_each(seat, &input_manager->seats, link) {
148 focus = seat_get_focus(seat);
149 focus_ws = focus;
150 if (focus_ws->type != C_WORKSPACE) {
151 focus_ws = container_parent(focus_ws, C_WORKSPACE);
152 }
153 if (focus_ws == new_workspace) {
154 struct sway_container *new_focus = seat_get_focus_inactive(seat,
155 new_workspace->sway_workspace->fullscreen);
156 seat_set_focus(seat, new_focus);
157 }
158 }
159 } 225 }
160 // Update workspace urgent state 226 if (container_is_floating(container)) {
161 struct sway_container *old_workspace = old_parent; 227 return;
162 if (old_workspace->type != C_WORKSPACE) {
163 old_workspace = container_parent(old_workspace, C_WORKSPACE);
164 }
165 if (new_workspace != old_workspace) {
166 workspace_detect_urgent(new_workspace);
167 if (old_workspace) {
168 workspace_detect_urgent(old_workspace);
169 }
170 } 228 }
171} 229 struct sway_workspace *old_workspace = container->workspace;
172 230
173static bool is_parallel(enum sway_container_layout layout, 231 container_detach(container);
174 enum movement_direction dir) { 232 container->width = container->height = 0;
175 switch (layout) { 233 container->saved_width = container->saved_height = 0;
176 case L_TABBED: 234
177 case L_HORIZ: 235 if (destination->view) {
178 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 236 container_add_sibling(destination, container, 1);
179 case L_STACKED: 237 } else {
180 case L_VERT: 238 container_add_child(destination, container);
181 return dir == MOVE_UP || dir == MOVE_DOWN;
182 default:
183 return false;
184 } 239 }
185}
186 240
187static enum movement_direction invert_movement(enum movement_direction dir) { 241 if (container->view) {
188 switch (dir) { 242 ipc_event_window(container, "move");
189 case MOVE_LEFT:
190 return MOVE_RIGHT;
191 case MOVE_RIGHT:
192 return MOVE_LEFT;
193 case MOVE_UP:
194 return MOVE_DOWN;
195 case MOVE_DOWN:
196 return MOVE_UP;
197 default:
198 sway_assert(0, "This function expects left|right|up|down");
199 return MOVE_LEFT;
200 } 243 }
201}
202 244
203static int move_offs(enum movement_direction move_dir) { 245 workspace_focus_fullscreen(destination->workspace);
204 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
205}
206 246
207/* Gets the index of the most extreme member based on the movement offset */ 247 // Update workspace urgent state
208static int container_limit(struct sway_container *container, 248 workspace_detect_urgent(destination->workspace);
209 enum movement_direction move_dir) { 249 if (old_workspace != destination->workspace) {
210 return move_offs(move_dir) < 0 ? 0 : container->children->length; 250 workspace_detect_urgent(old_workspace);
251 }
211} 252}
212 253
213/* Takes one child, sets it aside, wraps the rest of the children in a new 254/* Takes one child, sets it aside, wraps the rest of the children in a new
214 * container, switches the layout of the workspace, and drops the child back in. 255 * container, switches the layout of the workspace, and drops the child back in.
215 * In other words, rejigger it. */ 256 * In other words, rejigger it. */
216static void workspace_rejigger(struct sway_container *ws, 257static void workspace_rejigger(struct sway_workspace *ws,
217 struct sway_container *child, enum movement_direction move_dir) { 258 struct sway_container *child, enum movement_direction move_dir) {
218 struct sway_container *original_parent = child->parent; 259 if (!sway_assert(child->parent == NULL, "Expected a root child")) {
219 struct sway_container *new_parent = 260 return;
220 container_split(ws, ws->layout);
221
222 container_remove_child(child);
223 for (int i = 0; i < ws->children->length; ++i) {
224 struct sway_container *_child = ws->children->items[i];
225 container_move_to(new_parent, _child);
226 } 261 }
262 container_detach(child);
263 workspace_wrap_children(ws);
227 264
228 int index = move_offs(move_dir); 265 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1;
229 container_insert_child(ws, child, index < 0 ? 0 : 1); 266 workspace_insert_tiling(ws, child, index);
230 ws->layout = 267 ws->layout =
231 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 268 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
232 269 workspace_update_representation(ws);
233 container_flatten(ws);
234 container_reap_empty(original_parent);
235 container_create_notify(new_parent);
236} 270}
237 271
238static void move_out_of_tabs_stacks(struct sway_container *container, 272static void move_out_of_tabs_stacks(struct sway_container *container,
239 struct sway_container *current, enum movement_direction move_dir, 273 struct sway_container *current, enum movement_direction move_dir,
240 int offs) { 274 int offs) {
241 if (container->parent == current->parent 275 enum sway_container_layout layout = move_dir ==
242 && current->parent->children->length == 1) { 276 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
243 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); 277 list_t *siblings = container_get_siblings(container);
244 current->parent->layout = move_dir == 278 if (container == current && siblings->length == 1) {
245 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 279 wlr_log(WLR_DEBUG, "Changing layout of parent");
280 if (container->parent) {
281 container->parent->layout = layout;
282 container_update_representation(container);
283 } else {
284 container->workspace->layout = layout;
285 workspace_update_representation(container->workspace);
286 }
246 return; 287 return;
247 } 288 }
248 289
249 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); 290 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
250 bool is_workspace = current->parent->type == C_WORKSPACE; 291 if (container->parent) {
251 struct sway_container *new_parent = container_split(current->parent, 292 struct sway_container *new_parent =
252 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); 293 container_split(current->parent, layout);
253 if (is_workspace) {
254 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
255 } else {
256 container_insert_child(new_parent, container, offs < 0 ? 0 : 1); 294 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
257 container_reap_empty(new_parent->parent); 295 container_reap_empty(new_parent);
258 container_flatten(new_parent->parent); 296 container_flatten(new_parent);
297 } else {
298 // Changing a workspace
299 struct sway_workspace *workspace = container->workspace;
300 workspace_split(workspace, layout);
301 workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1);
259 } 302 }
260 container_create_notify(new_parent);
261 container_notify_subtree_changed(new_parent);
262} 303}
263 304
264static void container_move(struct sway_container *container, 305// Returns true if moved
265 enum movement_direction move_dir, int move_amt) { 306static bool container_move_in_direction(struct sway_container *container,
266 if (!sway_assert( 307 enum movement_direction move_dir) {
267 container->type != C_CONTAINER || container->type != C_VIEW,
268 "Can only move containers and views")) {
269 return;
270 }
271 int offs = move_offs(move_dir);
272
273 struct sway_container *sibling = NULL;
274 struct sway_container *current = container;
275 struct sway_container *parent = current->parent;
276 struct sway_container *top = &root_container;
277
278 // If moving a fullscreen view, only consider outputs 308 // If moving a fullscreen view, only consider outputs
279 if (container->is_fullscreen) { 309 if (container->is_fullscreen) {
280 current = container_parent(container, C_OUTPUT); 310 struct sway_output *new_output =
281 } else if (container_is_fullscreen_or_child(container) || 311 output_get_in_direction(container->workspace->output, move_dir);
282 container_is_floating_or_child(container)) { 312 if (!new_output) {
283 // If we've fullscreened a split container, only allow the child to move 313 return false;
284 // around within the fullscreen parent. 314 }
285 // Same with floating a split container. 315 struct sway_workspace *ws = output_get_active_workspace(new_output);
286 struct sway_container *ws = container_parent(container, C_WORKSPACE); 316 container_move_to_workspace(container, ws);
287 top = ws->sway_workspace->fullscreen; 317 return true;
288 }
289
290 struct sway_container *new_parent = container_flatten(parent);
291 if (new_parent != parent) {
292 // Special case: we were the last one in this container, so leave
293 return;
294 } 318 }
295 319
296 while (!sibling) { 320 // If container is in a split container by itself, move out of the split
297 if (current == top) { 321 if (container->parent) {
298 return; 322 struct sway_container *new_parent =
323 container_flatten(container->parent);
324 if (new_parent != container->parent) {
325 return true;
299 } 326 }
327 }
300 328
301 parent = current->parent; 329 // Look for a suitable *container* sibling or parent.
302 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, 330 // The below loop stops once we hit the workspace because current->parent
303 container_type_to_str(current->type), current->name); 331 // is NULL for the topmost containers in a workspace.
304 332 struct sway_container *current = container;
305 int index = container_sibling_index(current); 333 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
306 334
307 switch (current->type) { 335 while (current) {
308 case C_OUTPUT: { 336 struct sway_container *parent = current->parent;
309 enum wlr_direction wlr_dir = 0; 337 list_t *siblings = container_get_siblings(current);
310 if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), 338 enum sway_container_layout layout = container_parent_layout(current);
311 "got invalid direction: %d", move_dir)) { 339 int index = list_find(siblings, current);
312 return; 340 int desired = index + offs;
313 } 341
314 double ref_lx = current->x + current->width / 2; 342 if (is_parallel(layout, move_dir)) {
315 double ref_ly = current->y + current->height / 2; 343 if (desired == -1 || desired == siblings->length) {
316 struct wlr_output *next = wlr_output_layout_adjacent_output( 344 if (current->parent == container->parent) {
317 root_container.sway_root->output_layout, wlr_dir, 345 if (!(parent && parent->is_fullscreen) &&
318 current->sway_output->wlr_output, ref_lx, ref_ly); 346 (layout == L_TABBED || layout == L_STACKED)) {
319 if (!next) { 347 move_out_of_tabs_stacks(container, current,
320 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); 348 move_dir, offs);
321 return; 349 return true;
322 }
323 struct sway_output *next_output = next->data;
324 current = next_output->swayc;
325 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
326 // Select workspace and get outta here
327 current = seat_get_focus_inactive(
328 config->handler_context.seat, current);
329 if (current->type != C_WORKSPACE) {
330 current = container_parent(current, C_WORKSPACE);
331 }
332 sibling = current;
333 break;
334 }
335 case C_WORKSPACE:
336 if (!is_parallel(current->layout, move_dir)) {
337 if (current->children->length >= 2) {
338 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
339 current->children->length);
340 workspace_rejigger(current, container, move_dir);
341 return;
342 } else {
343 wlr_log(WLR_DEBUG, "Selecting output");
344 current = current->parent;
345 }
346 } else if (current->layout == L_TABBED
347 || current->layout == L_STACKED) {
348 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
349 workspace_rejigger(current, container, move_dir);
350 } else {
351 wlr_log(WLR_DEBUG, "Selecting output");
352 current = current->parent;
353 }
354 break;
355 case C_CONTAINER:
356 case C_VIEW:
357 if (is_parallel(parent->layout, move_dir)) {
358 if ((index == parent->children->length - 1 && offs > 0)
359 || (index == 0 && offs < 0)) {
360 if (current->parent == container->parent) {
361 if (!parent->is_fullscreen &&
362 (parent->layout == L_TABBED ||
363 parent->layout == L_STACKED)) {
364 move_out_of_tabs_stacks(container, current,
365 move_dir, offs);
366 return;
367 } else {
368 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
369 current = current->parent;
370 }
371 } else { 350 } else {
372 wlr_log(WLR_DEBUG, "Hit limit, " 351 current = current->parent;
373 "promoting descendant to sibling"); 352 continue;
374 // Special case 353 }
354 } else {
355 // Special case
356 if (current->parent) {
375 container_insert_child(current->parent, container, 357 container_insert_child(current->parent, container,
376 index + (offs < 0 ? 0 : 1)); 358 index + (offs < 0 ? 0 : 1));
377 container->width = container->height = 0; 359 } else {
378 return; 360 workspace_insert_tiling(current->workspace, container,
361 index + (offs < 0 ? 0 : 1));
379 } 362 }
380 } else { 363 return true;
381 sibling = parent->children->items[index + offs];
382 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
383 } 364 }
384 } else if (!parent->is_fullscreen && (parent->layout == L_TABBED ||
385 parent->layout == L_STACKED)) {
386 move_out_of_tabs_stacks(container, current, move_dir, offs);
387 return;
388 } else { 365 } else {
389 wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); 366 // Container can move within its siblings
390 current = current->parent; 367 container_move_to_container_from_direction(container,
368 siblings->items[desired], move_dir);
369 return true;
391 } 370 }
392 break; 371 } else if (!(parent && parent->is_fullscreen) &&
393 default: 372 (layout == L_TABBED || layout == L_STACKED)) {
394 sway_assert(0, "Not expecting to see container of type %s here", 373 move_out_of_tabs_stacks(container, current, move_dir, offs);
395 container_type_to_str(current->type)); 374 return true;
396 return;
397 } 375 }
398 }
399 376
400 // Part two: move stuff around 377 current = current->parent;
401 int index = container_sibling_index(container);
402 struct sway_container *old_parent = container->parent;
403 378
404 while (sibling) { 379 // Don't allow containers to move out of their
405 switch (sibling->type) { 380 // fullscreen or floating parent
406 case C_VIEW: 381 if (current &&
407 if (sibling->parent == container->parent) { 382 (current->is_fullscreen || container_is_floating(current))) {
408 wlr_log(WLR_DEBUG, "Swapping siblings"); 383 return false;
409 sibling->parent->children->items[index + offs] = container;
410 sibling->parent->children->items[index] = sibling;
411 } else {
412 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
413 container_insert_child(sibling->parent, container,
414 container_sibling_index(sibling) + (offs > 0 ? 0 : 1));
415 container->width = container->height = 0;
416 }
417 sibling = NULL;
418 break;
419 case C_WORKSPACE: // Note: only in the case of moving between outputs
420 case C_CONTAINER:
421 if (is_parallel(sibling->layout, move_dir)) {
422 int limit = container_limit(sibling, invert_movement(move_dir));
423 wlr_log(WLR_DEBUG, "limit: %d", limit);
424 wlr_log(WLR_DEBUG,
425 "Reparenting container (parallel) to index %d "
426 "(move dir: %d)", limit, move_dir);
427 container_insert_child(sibling, container, limit);
428 container->width = container->height = 0;
429 sibling = NULL;
430 } else {
431 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
432 struct sway_container *focus_inactive = seat_get_focus_inactive(
433 config->handler_context.seat, sibling);
434 if (focus_inactive && focus_inactive != sibling) {
435 while (focus_inactive->parent != sibling) {
436 focus_inactive = focus_inactive->parent;
437 }
438 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
439 focus_inactive->id);
440 sibling = focus_inactive;
441 continue;
442 } else if (sibling->children->length) {
443 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
444 container_remove_child(container);
445 container_add_sibling(sibling->children->items[0], container);
446 } else {
447 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
448 container_remove_child(container);
449 container_add_child(sibling, container);
450 }
451 container->width = container->height = 0;
452 sibling = NULL;
453 }
454 break;
455 default:
456 sway_assert(0, "Not expecting to see container of type %s here",
457 container_type_to_str(sibling->type));
458 return;
459 } 384 }
460 } 385 }
461 386
462 container_notify_subtree_changed(old_parent); 387 // Maybe rejigger the workspace
463 container_notify_subtree_changed(container->parent); 388 struct sway_workspace *ws = container->workspace;
464 389 if (!is_parallel(ws->layout, move_dir)) {
465 if (container->type == C_VIEW) { 390 if (ws->tiling->length >= 2) {
466 ipc_event_window(container, "move"); 391 workspace_rejigger(ws, container, move_dir);
467 } 392 return true;
468 393 }
469 if (old_parent) { 394 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
470 seat_set_focus(config->handler_context.seat, old_parent); 395 workspace_rejigger(ws, container, move_dir);
471 seat_set_focus(config->handler_context.seat, container); 396 return true;
472 } 397 }
473 398
474 struct sway_container *last_ws = old_parent; 399 // Try adjacent output
475 struct sway_container *next_ws = container->parent; 400 struct sway_output *output =
476 if (last_ws && last_ws->type != C_WORKSPACE) { 401 output_get_in_direction(container->workspace->output, move_dir);
477 last_ws = container_parent(last_ws, C_WORKSPACE); 402 if (output) {
478 } 403 struct sway_workspace *ws = output_get_active_workspace(output);
479 if (next_ws && next_ws->type != C_WORKSPACE) { 404 container_move_to_workspace_from_direction(container, ws, move_dir);
480 next_ws = container_parent(next_ws, C_WORKSPACE); 405 return true;
481 } 406 }
482 if (last_ws && next_ws && last_ws != next_ws) { 407 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
483 ipc_event_workspace(last_ws, next_ws, "focus"); 408 return false;
484 workspace_detect_urgent(last_ws);
485 workspace_detect_urgent(next_ws);
486 }
487 container_end_mouse_operation(container);
488} 409}
489 410
490static struct cmd_results *cmd_move_container(struct sway_container *current, 411static struct cmd_results *cmd_move_container(int argc, char **argv) {
491 int argc, char **argv) {
492 struct cmd_results *error = NULL; 412 struct cmd_results *error = NULL;
493 if ((error = checkarg(argc, "move container/window", 413 if ((error = checkarg(argc, "move container/window",
494 EXPECTED_AT_LEAST, 3))) { 414 EXPECTED_AT_LEAST, 3))) {
495 return error; 415 return error;
496 } 416 }
497 417
498 if (current->type == C_WORKSPACE) { 418 struct sway_node *node = config->handler_context.node;
499 if (current->children->length == 0) { 419 struct sway_workspace *workspace = config->handler_context.workspace;
420 struct sway_container *container = config->handler_context.container;
421 if (node->type == N_WORKSPACE) {
422 if (workspace->tiling->length == 0) {
500 return cmd_results_new(CMD_FAILURE, "move", 423 return cmd_results_new(CMD_FAILURE, "move",
501 "Can't move an empty workspace"); 424 "Can't move an empty workspace");
502 } 425 }
503 current = workspace_wrap_children(current); 426 container = workspace_wrap_children(workspace);
504 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
505 return cmd_results_new(CMD_FAILURE, "move",
506 "Can only move containers and views.");
507 } 427 }
508 428
509 bool no_auto_back_and_forth = false; 429 bool no_auto_back_and_forth = false;
@@ -530,15 +450,15 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
530 } 450 }
531 451
532 struct sway_seat *seat = config->handler_context.seat; 452 struct sway_seat *seat = config->handler_context.seat;
533 struct sway_container *old_parent = current->parent; 453 struct sway_container *old_parent = container->parent;
534 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 454 struct sway_workspace *old_ws = container->workspace;
535 struct sway_container *old_output = container_parent(current, C_OUTPUT); 455 struct sway_output *old_output = old_ws->output;
536 struct sway_container *destination = NULL; 456 struct sway_node *destination = NULL;
537 457
538 // determine destination 458 // determine destination
539 if (strcasecmp(argv[1], "workspace") == 0) { 459 if (strcasecmp(argv[1], "workspace") == 0) {
540 // move container to workspace x 460 // move container to workspace x
541 struct sway_container *ws = NULL; 461 struct sway_workspace *ws = NULL;
542 char *ws_name = NULL; 462 char *ws_name = NULL;
543 if (strcasecmp(argv[2], "next") == 0 || 463 if (strcasecmp(argv[2], "next") == 0 ||
544 strcasecmp(argv[2], "prev") == 0 || 464 strcasecmp(argv[2], "prev") == 0 ||
@@ -588,8 +508,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
588 // We have to create the workspace, but if the container is 508 // We have to create the workspace, but if the container is
589 // sticky and the workspace is going to be created on the same 509 // sticky and the workspace is going to be created on the same
590 // output, we'll bail out first. 510 // output, we'll bail out first.
591 if (current->is_sticky) { 511 if (container->is_sticky) {
592 struct sway_container *new_output = 512 struct sway_output *new_output =
593 workspace_get_initial_output(ws_name); 513 workspace_get_initial_output(ws_name);
594 if (old_output == new_output) { 514 if (old_output == new_output) {
595 free(ws_name); 515 free(ws_name);
@@ -601,105 +521,113 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
601 ws = workspace_create(NULL, ws_name); 521 ws = workspace_create(NULL, ws_name);
602 } 522 }
603 free(ws_name); 523 free(ws_name);
604 destination = seat_get_focus_inactive(seat, ws); 524 destination = seat_get_focus_inactive(seat, &ws->node);
605 } else if (strcasecmp(argv[1], "output") == 0) { 525 } else if (strcasecmp(argv[1], "output") == 0) {
606 struct sway_container *dest_output = output_in_direction(argv[2], 526 struct sway_output *new_output = output_in_direction(argv[2],
607 old_output->sway_output->wlr_output, current->x, current->y); 527 old_output->wlr_output, container->x, container->y);
608 if (!dest_output) { 528 if (!new_output) {
609 return cmd_results_new(CMD_FAILURE, "move workspace", 529 return cmd_results_new(CMD_FAILURE, "move workspace",
610 "Can't find output with name/direction '%s'", argv[2]); 530 "Can't find output with name/direction '%s'", argv[2]);
611 } 531 }
612 destination = seat_get_focus_inactive(seat, dest_output); 532 destination = seat_get_focus_inactive(seat, &new_output->node);
613 if (!destination) {
614 // We've never been to this output before
615 destination = dest_output->children->items[0];
616 }
617 } else if (strcasecmp(argv[1], "mark") == 0) { 533 } else if (strcasecmp(argv[1], "mark") == 0) {
618 struct sway_view *dest_view = view_find_mark(argv[2]); 534 struct sway_view *dest_view = view_find_mark(argv[2]);
619 if (dest_view == NULL) { 535 if (dest_view == NULL) {
620 return cmd_results_new(CMD_FAILURE, "move", 536 return cmd_results_new(CMD_FAILURE, "move",
621 "Mark '%s' not found", argv[2]); 537 "Mark '%s' not found", argv[2]);
622 } 538 }
623 destination = dest_view->swayc; 539 destination = &dest_view->container->node;
624 } else { 540 } else {
625 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 541 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
626 } 542 }
627 543
628 struct sway_container *new_output = destination->type == C_OUTPUT ? 544 if (container->is_sticky &&
629 destination : container_parent(destination, C_OUTPUT); 545 node_has_ancestor(destination, &old_output->node)) {
630 if (current->is_sticky && old_output == new_output) {
631 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " 546 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky "
632 "container to another workspace on the same output"); 547 "container to another workspace on the same output");
633 } 548 }
634 549
635 struct sway_container *new_output_last_ws = old_output == new_output ? 550 struct sway_output *new_output = node_get_output(destination);
636 NULL : seat_get_active_child(seat, new_output); 551 struct sway_workspace *new_output_last_ws = old_output == new_output ?
637 struct sway_container *new_workspace = destination->type == C_WORKSPACE ? 552 NULL : output_get_active_workspace(new_output);
638 destination : container_parent(destination, C_WORKSPACE);
639 553
640 // move container, arrange windows and return focus 554 // move container, arrange windows and return focus
641 container_move_to(current, destination); 555 switch (destination->type) {
556 case N_WORKSPACE:
557 container_move_to_workspace(container, destination->sway_workspace);
558 break;
559 case N_OUTPUT: {
560 struct sway_output *output = destination->sway_output;
561 struct sway_workspace *ws = output_get_active_workspace(output);
562 container_move_to_workspace(container, ws);
563 }
564 break;
565 case N_CONTAINER:
566 container_move_to_container(container, destination->sway_container);
567 break;
568 case N_ROOT:
569 break;
570 }
571 struct sway_workspace *new_workspace =
572 output_get_active_workspace(new_output);
642 if (new_output_last_ws && new_output_last_ws != new_workspace) { 573 if (new_output_last_ws && new_output_last_ws != new_workspace) {
643 // change focus on destination output back to its last active workspace 574 // change focus on destination output back to its last active workspace
644 struct sway_container *new_output_last_focus = 575 struct sway_node *new_output_last_focus =
645 seat_get_focus_inactive(seat, new_output_last_ws); 576 seat_get_focus_inactive(seat, &new_output_last_ws->node);
646 seat_set_focus_warp(seat, new_output_last_focus, false, false); 577 seat_set_focus_warp(seat, new_output_last_focus, false, false);
647 } 578 }
648 struct sway_container *focus = seat_get_focus_inactive(seat, old_parent); 579
580 struct sway_node *focus = NULL;
581 if (old_parent) {
582 focus = seat_get_focus_inactive(seat, &old_parent->node);
583 } else {
584 focus = seat_get_focus_inactive(seat, &old_ws->node);
585 }
649 seat_set_focus_warp(seat, focus, true, false); 586 seat_set_focus_warp(seat, focus, true, false);
650 container_reap_empty(old_parent);
651 container_reap_empty(destination->parent);
652 587
653 // TODO: Ideally we would arrange the surviving parent after reaping, 588 if (old_parent) {
654 // but container_reap_empty does not return it, so we arrange the 589 container_reap_empty(old_parent);
655 // workspace instead. 590 } else {
656 arrange_windows(old_ws); 591 workspace_consider_destroy(old_ws);
657 arrange_windows(destination->parent); 592 }
593
594 arrange_workspace(old_ws);
595 arrange_node(node_get_parent(destination));
658 596
659 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 597 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
660} 598}
661 599
662static void workspace_move_to_output(struct sway_container *workspace, 600static void workspace_move_to_output(struct sway_workspace *workspace,
663 struct sway_container *output) { 601 struct sway_output *output) {
664 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 602 if (workspace->output == output) {
665 return; 603 return;
666 } 604 }
667 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 605 struct sway_output *old_output = workspace->output;
668 return; 606 workspace_detach(workspace);
669 } 607 struct sway_workspace *new_output_old_ws =
670 if (workspace->parent == output) { 608 output_get_active_workspace(output);
671 return;
672 }
673 struct sway_container *old_output = container_remove_child(workspace);
674 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
675 struct sway_container *new_output_focus =
676 seat_get_focus_inactive(seat, output);
677 609
678 container_add_child(output, workspace); 610 output_add_workspace(output, workspace);
679 611
680 // If moving the last workspace from the old output, create a new workspace 612 // If moving the last workspace from the old output, create a new workspace
681 // on the old output 613 // on the old output
682 if (old_output->children->length == 0) { 614 struct sway_seat *seat = config->handler_context.seat;
683 char *ws_name = workspace_next_name(old_output->name); 615 if (old_output->workspaces->length == 0) {
684 struct sway_container *ws = workspace_create(old_output, ws_name); 616 char *ws_name = workspace_next_name(old_output->wlr_output->name);
617 struct sway_workspace *ws = workspace_create(old_output, ws_name);
685 free(ws_name); 618 free(ws_name);
686 seat_set_focus(seat, ws); 619 seat_set_focus(seat, &ws->node);
687 } 620 }
688 621
689 // Try to remove an empty workspace from the destination output. 622 workspace_consider_destroy(new_output_old_ws);
690 container_reap_empty(new_output_focus);
691 623
692 output_sort_workspaces(output); 624 output_sort_workspaces(output);
693 seat_set_focus(seat, output); 625 seat_set_focus(seat, &output->node);
694 workspace_output_raise_priority(workspace, old_output, output); 626 workspace_output_raise_priority(workspace, old_output, output);
695 ipc_event_workspace(NULL, workspace, "move"); 627 ipc_event_workspace(NULL, workspace, "move");
696
697 container_notify_subtree_changed(old_output);
698 container_notify_subtree_changed(output);
699} 628}
700 629
701static struct cmd_results *cmd_move_workspace(struct sway_container *current, 630static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
702 int argc, char **argv) {
703 struct cmd_results *error = NULL; 631 struct cmd_results *error = NULL;
704 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { 632 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
705 return error; 633 return error;
@@ -716,27 +644,25 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
716 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 644 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
717 } 645 }
718 646
719 struct sway_container *source = container_parent(current, C_OUTPUT); 647 struct sway_workspace *workspace = config->handler_context.workspace;
720 int center_x = current->width / 2 + current->x, 648 struct sway_output *old_output = workspace->output;
721 center_y = current->height / 2 + current->y; 649 int center_x = workspace->width / 2 + workspace->x,
722 struct sway_container *destination = output_in_direction(argv[2], 650 center_y = workspace->height / 2 + workspace->y;
723 source->sway_output->wlr_output, center_x, center_y); 651 struct sway_output *new_output = output_in_direction(argv[2],
724 if (!destination) { 652 old_output->wlr_output, center_x, center_y);
653 if (!new_output) {
725 return cmd_results_new(CMD_FAILURE, "move workspace", 654 return cmd_results_new(CMD_FAILURE, "move workspace",
726 "Can't find output with name/direction '%s'", argv[2]); 655 "Can't find output with name/direction '%s'", argv[2]);
727 } 656 }
728 if (current->type != C_WORKSPACE) { 657 workspace_move_to_output(workspace, new_output);
729 current = container_parent(current, C_WORKSPACE);
730 }
731 workspace_move_to_output(current, destination);
732 658
733 arrange_windows(source); 659 arrange_output(old_output);
734 arrange_windows(destination); 660 arrange_output(new_output);
735 661
736 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 662 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
737} 663}
738 664
739static struct cmd_results *move_in_direction(struct sway_container *container, 665static struct cmd_results *cmd_move_in_direction(
740 enum movement_direction direction, int argc, char **argv) { 666 enum movement_direction direction, int argc, char **argv) {
741 int move_amt = 10; 667 int move_amt = 10;
742 if (argc > 1) { 668 if (argc > 1) {
@@ -748,7 +674,8 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
748 } 674 }
749 } 675 }
750 676
751 if (container->type == C_WORKSPACE) { 677 struct sway_container *container = config->handler_context.container;
678 if (!container) {
752 return cmd_results_new(CMD_FAILURE, "move", 679 return cmd_results_new(CMD_FAILURE, "move",
753 "Cannot move workspaces in a direction"); 680 "Cannot move workspaces in a direction");
754 } 681 }
@@ -780,20 +707,34 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
780 container_floating_move_to(container, lx, ly); 707 container_floating_move_to(container, lx, ly);
781 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 708 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
782 } 709 }
783 // For simplicity, we'll arrange the entire workspace. The reason for this 710 struct sway_workspace *old_ws = container->workspace;
784 // is moving the container might reap the old parent, and container_move
785 // does not return a surviving parent.
786 // TODO: Make container_move return the surviving parent so we can arrange
787 // just that.
788 struct sway_container *old_ws = container_parent(container, C_WORKSPACE);
789 container_move(container, direction, move_amt);
790 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
791 711
792 arrange_windows(old_ws); 712 if (!container_move_in_direction(container, direction)) {
713 // Container didn't move
714 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
715 }
716
717 struct sway_workspace *new_ws = container->workspace;
718
719 arrange_workspace(old_ws);
793 if (new_ws != old_ws) { 720 if (new_ws != old_ws) {
794 arrange_windows(new_ws); 721 arrange_workspace(new_ws);
722 }
723
724 if (container->view) {
725 ipc_event_window(container, "move");
795 } 726 }
796 727
728 seat_set_focus(config->handler_context.seat, &new_ws->node);
729 seat_set_focus(config->handler_context.seat, &container->node);
730
731 if (old_ws != new_ws) {
732 ipc_event_workspace(old_ws, new_ws, "focus");
733 workspace_detect_urgent(old_ws);
734 workspace_detect_urgent(new_ws);
735 }
736 container_end_mouse_operation(container);
737
797 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 738 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
798} 739}
799 740
@@ -802,9 +743,9 @@ static const char *expected_position_syntax =
802 "'move [absolute] position center' or " 743 "'move [absolute] position center' or "
803 "'move position cursor|mouse|pointer'"; 744 "'move position cursor|mouse|pointer'";
804 745
805static struct cmd_results *move_to_position(struct sway_container *container, 746static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
806 int argc, char **argv) { 747 struct sway_container *container = config->handler_context.container;
807 if (!container_is_floating(container)) { 748 if (!container || !container_is_floating(container)) {
808 return cmd_results_new(CMD_FAILURE, "move", 749 return cmd_results_new(CMD_FAILURE, "move",
809 "Only floating containers " 750 "Only floating containers "
810 "can be moved to an absolute position"); 751 "can be moved to an absolute position");
@@ -842,10 +783,10 @@ static struct cmd_results *move_to_position(struct sway_container *container,
842 } else if (strcmp(argv[0], "center") == 0) { 783 } else if (strcmp(argv[0], "center") == 0) {
843 double lx, ly; 784 double lx, ly;
844 if (absolute) { 785 if (absolute) {
845 lx = root_container.x + (root_container.width - container->width) / 2; 786 lx = root->x + (root->width - container->width) / 2;
846 ly = root_container.y + (root_container.height - container->height) / 2; 787 ly = root->y + (root->height - container->height) / 2;
847 } else { 788 } else {
848 struct sway_container *ws = container_parent(container, C_WORKSPACE); 789 struct sway_workspace *ws = container->workspace;
849 lx = ws->x + (ws->width - container->width) / 2; 790 lx = ws->x + (ws->width - container->width) / 2;
850 ly = ws->y + (ws->height - container->height) / 2; 791 ly = ws->y + (ws->height - container->height) / 2;
851 } 792 }
@@ -881,30 +822,31 @@ static struct cmd_results *move_to_position(struct sway_container *container,
881 } 822 }
882 823
883 if (!absolute) { 824 if (!absolute) {
884 struct sway_container *ws = container_parent(container, C_WORKSPACE); 825 lx += container->workspace->x;
885 lx += ws->x; 826 ly += container->workspace->y;
886 ly += ws->y;
887 } 827 }
888 container_floating_move_to(container, lx, ly); 828 container_floating_move_to(container, lx, ly);
889 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 829 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
890} 830}
891 831
892static struct cmd_results *move_to_scratchpad(struct sway_container *con) { 832static struct cmd_results *cmd_move_to_scratchpad(void) {
893 if (con->type == C_WORKSPACE && con->children->length == 0) { 833 struct sway_node *node = config->handler_context.node;
834 struct sway_container *con = config->handler_context.container;
835 struct sway_workspace *ws = config->handler_context.workspace;
836 if (node->type == N_WORKSPACE && ws->tiling->length == 0) {
894 return cmd_results_new(CMD_INVALID, "move", 837 return cmd_results_new(CMD_INVALID, "move",
895 "Can't move an empty workspace to the scratchpad"); 838 "Can't move an empty workspace to the scratchpad");
896 } 839 }
897 if (con->type == C_WORKSPACE) { 840 if (node->type == N_WORKSPACE) {
898 // Wrap the workspace's children in a container 841 // Wrap the workspace's children in a container
899 struct sway_container *workspace = con; 842 con = workspace_wrap_children(ws);
900 con = workspace_wrap_children(con); 843 ws->layout = L_HORIZ;
901 workspace->layout = L_HORIZ;
902 } 844 }
903 845
904 // If the container is in a floating split container, 846 // If the container is in a floating split container,
905 // operate on the split container instead of the child. 847 // operate on the split container instead of the child.
906 if (container_is_floating_or_child(con)) { 848 if (container_is_floating_or_child(con)) {
907 while (con->parent->type != C_WORKSPACE) { 849 while (con->parent) {
908 con = con->parent; 850 con = con->parent;
909 } 851 }
910 } 852 }
@@ -922,32 +864,31 @@ struct cmd_results *cmd_move(int argc, char **argv) {
922 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 864 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
923 return error; 865 return error;
924 } 866 }
925 struct sway_container *current = config->handler_context.current_container;
926 867
927 if (strcasecmp(argv[0], "left") == 0) { 868 if (strcasecmp(argv[0], "left") == 0) {
928 return move_in_direction(current, MOVE_LEFT, argc, argv); 869 return cmd_move_in_direction(MOVE_LEFT, argc, argv);
929 } else if (strcasecmp(argv[0], "right") == 0) { 870 } else if (strcasecmp(argv[0], "right") == 0) {
930 return move_in_direction(current, MOVE_RIGHT, argc, argv); 871 return cmd_move_in_direction(MOVE_RIGHT, argc, argv);
931 } else if (strcasecmp(argv[0], "up") == 0) { 872 } else if (strcasecmp(argv[0], "up") == 0) {
932 return move_in_direction(current, MOVE_UP, argc, argv); 873 return cmd_move_in_direction(MOVE_UP, argc, argv);
933 } else if (strcasecmp(argv[0], "down") == 0) { 874 } else if (strcasecmp(argv[0], "down") == 0) {
934 return move_in_direction(current, MOVE_DOWN, argc, argv); 875 return cmd_move_in_direction(MOVE_DOWN, argc, argv);
935 } else if ((strcasecmp(argv[0], "container") == 0 876 } else if ((strcasecmp(argv[0], "container") == 0
936 || strcasecmp(argv[0], "window") == 0) || 877 || strcasecmp(argv[0], "window") == 0) ||
937 (strcasecmp(argv[0], "--no-auto-back-and-forth") && 878 (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2
938 (strcasecmp(argv[0], "container") == 0 879 && (strcasecmp(argv[1], "container") == 0
939 || strcasecmp(argv[0], "window") == 0))) { 880 || strcasecmp(argv[1], "window") == 0))) {
940 return cmd_move_container(current, argc, argv); 881 return cmd_move_container(argc, argv);
941 } else if (strcasecmp(argv[0], "workspace") == 0) { 882 } else if (strcasecmp(argv[0], "workspace") == 0) {
942 return cmd_move_workspace(current, argc, argv); 883 return cmd_move_workspace(argc, argv);
943 } else if (strcasecmp(argv[0], "scratchpad") == 0 884 } else if (strcasecmp(argv[0], "scratchpad") == 0
944 || (strcasecmp(argv[0], "to") == 0 && argc == 2 885 || (strcasecmp(argv[0], "to") == 0 && argc == 2
945 && strcasecmp(argv[1], "scratchpad") == 0)) { 886 && strcasecmp(argv[1], "scratchpad") == 0)) {
946 return move_to_scratchpad(current); 887 return cmd_move_to_scratchpad();
947 } else if (strcasecmp(argv[0], "position") == 0) { 888 } else if (strcasecmp(argv[0], "position") == 0) {
948 return move_to_position(current, argc, argv); 889 return cmd_move_to_position(argc, argv);
949 } else if (strcasecmp(argv[0], "absolute") == 0) { 890 } else if (strcasecmp(argv[0], "absolute") == 0) {
950 return move_to_position(current, argc, argv); 891 return cmd_move_to_position(argc, argv);
951 } else { 892 } else {
952 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 893 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
953 } 894 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 68fd9f42..9cdaad7f 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -19,8 +19,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
19 return error; 19 return error;
20 } 20 }
21 21
22 struct sway_container *con = 22 struct sway_container *con = config->handler_context.container;
23 config->handler_context.current_container;
24 23
25 float opacity = 0.0f; 24 float opacity = 0.0f;
26 25
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index f8ca374d..36fb9092 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -42,7 +42,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
42 } 42 }
43 list_free(bar_ids); 43 list_free(bar_ids);
44 44
45 arrange_windows(&root_container); 45 arrange_root();
46 46
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
48} 48}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 21d2aa64..d982f941 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -25,14 +25,11 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 } 25 }
26 26
27 int argn = 1; 27 int argn = 1;
28 struct sway_container *workspace; 28 struct sway_workspace *workspace = NULL;
29 29
30 if (strcasecmp(argv[1], "to") == 0) { 30 if (strcasecmp(argv[1], "to") == 0) {
31 // 'rename workspace to new_name' 31 // 'rename workspace to new_name'
32 workspace = config->handler_context.current_container; 32 workspace = config->handler_context.workspace;
33 if (workspace->type != C_WORKSPACE) {
34 workspace = container_parent(workspace, C_WORKSPACE);
35 }
36 } else if (strcasecmp(argv[1], "number") == 0) { 33 } else if (strcasecmp(argv[1], "number") == 0) {
37 // 'rename workspace number x to new_name' 34 // 'rename workspace number x to new_name'
38 if (!isdigit(argv[2][0])) { 35 if (!isdigit(argv[2][0])) {
@@ -78,7 +75,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
78 return cmd_results_new(CMD_INVALID, "rename", 75 return cmd_results_new(CMD_INVALID, "rename",
79 "Cannot use special workspace name '%s'", argv[argn]); 76 "Cannot use special workspace name '%s'", argv[argn]);
80 } 77 }
81 struct sway_container *tmp_workspace = workspace_by_name(new_name); 78 struct sway_workspace *tmp_workspace = workspace_by_name(new_name);
82 if (tmp_workspace) { 79 if (tmp_workspace) {
83 free(new_name); 80 free(new_name);
84 return cmd_results_new(CMD_INVALID, "rename", 81 return cmd_results_new(CMD_INVALID, "rename",
@@ -89,7 +86,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
89 free(workspace->name); 86 free(workspace->name);
90 workspace->name = new_name; 87 workspace->name = new_name;
91 88
92 output_sort_workspaces(workspace->parent); 89 output_sort_workspaces(workspace->output);
93 ipc_event_workspace(NULL, workspace, "rename"); 90 ipc_event_workspace(NULL, workspace, "rename");
94 91
95 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 92 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ad659ef5..99e9dbda 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -10,6 +10,7 @@
10#include "sway/commands.h" 10#include "sway/commands.h"
11#include "sway/tree/arrange.h" 11#include "sway/tree/arrange.h"
12#include "sway/tree/view.h" 12#include "sway/tree/view.h"
13#include "sway/tree/workspace.h"
13#include "log.h" 14#include "log.h"
14 15
15static const int MIN_SANE_W = 100, MIN_SANE_H = 60; 16static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
@@ -75,7 +76,7 @@ static int parse_resize_amount(int argc, char **argv,
75 76
76static void calculate_constraints(int *min_width, int *max_width, 77static void calculate_constraints(int *min_width, int *max_width,
77 int *min_height, int *max_height) { 78 int *min_height, int *max_height) {
78 struct sway_container *con = config->handler_context.current_container; 79 struct sway_container *con = config->handler_context.container;
79 80
80 if (config->floating_minimum_width == -1) { // no minimum 81 if (config->floating_minimum_width == -1) { // no minimum
81 *min_width = 0; 82 *min_width = 0;
@@ -96,8 +97,7 @@ static void calculate_constraints(int *min_width, int *max_width,
96 if (config->floating_maximum_width == -1) { // no maximum 97 if (config->floating_maximum_width == -1) { // no maximum
97 *max_width = INT_MAX; 98 *max_width = INT_MAX;
98 } else if (config->floating_maximum_width == 0) { // automatic 99 } else if (config->floating_maximum_width == 0) { // automatic
99 struct sway_container *ws = container_parent(con, C_WORKSPACE); 100 *max_width = con->workspace->width;
100 *max_width = ws->width;
101 } else { 101 } else {
102 *max_width = config->floating_maximum_width; 102 *max_width = config->floating_maximum_width;
103 } 103 }
@@ -105,8 +105,7 @@ static void calculate_constraints(int *min_width, int *max_width,
105 if (config->floating_maximum_height == -1) { // no maximum 105 if (config->floating_maximum_height == -1) { // no maximum
106 *max_height = INT_MAX; 106 *max_height = INT_MAX;
107 } else if (config->floating_maximum_height == 0) { // automatic 107 } else if (config->floating_maximum_height == 0) { // automatic
108 struct sway_container *ws = container_parent(con, C_WORKSPACE); 108 *max_height = con->workspace->height;
109 *max_height = ws->height;
110 } else { 109 } else {
111 *max_height = config->floating_maximum_height; 110 *max_height = config->floating_maximum_height;
112 } 111 }
@@ -191,11 +190,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
191 normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT; 190 normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
192 int minor_weight = 0; 191 int minor_weight = 0;
193 int major_weight = 0; 192 int major_weight = 0;
194 while (parent->parent) { 193 while (parent) {
195 struct sway_container *next = parent->parent; 194 list_t *siblings = container_get_siblings(parent);
196 if (next->layout == parallel_layout) { 195 if (container_parent_layout(parent) == parallel_layout) {
197 for (int i = 0; i < next->children->length; i++) { 196 for (int i = 0; i < siblings->length; i++) {
198 struct sway_container *sibling = next->children->items[i]; 197 struct sway_container *sibling = siblings->items[i];
199 198
200 int sibling_pos = parallel_coord(sibling, axis); 199 int sibling_pos = parallel_coord(sibling, axis);
201 int focused_pos = parallel_coord(focused, axis); 200 int focused_pos = parallel_coord(focused, axis);
@@ -213,17 +212,13 @@ static void resize_tiled(struct sway_container *parent, int amount,
213 break; 212 break;
214 } 213 }
215 } 214 }
216 parent = next; 215 parent = parent->parent;
217 } 216 }
218 217 if (!parent) {
219 if (parent->type == C_ROOT) { 218 // Can't resize in this direction
220 return; 219 return;
221 } 220 }
222 221
223 wlr_log(WLR_DEBUG,
224 "Found the proper parent: %p. It has %d l conts, and %d r conts",
225 parent->parent, minor_weight, major_weight);
226
227 // Implement up/down/left/right direction by zeroing one of the weights, 222 // Implement up/down/left/right direction by zeroing one of the weights,
228 // then setting the axis to be horizontal or vertical 223 // then setting the axis to be horizontal or vertical
229 if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) { 224 if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) {
@@ -237,9 +232,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
237 232
238 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks 233 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
239 // ^ ????? 234 // ^ ?????
235 list_t *siblings = container_get_siblings(parent);
240 236
241 for (int i = 0; i < parent->parent->children->length; i++) { 237 for (int i = 0; i < siblings->length; i++) {
242 struct sway_container *sibling = parent->parent->children->items[i]; 238 struct sway_container *sibling = siblings->items[i];
243 239
244 int sibling_pos = parallel_coord(sibling, axis); 240 int sibling_pos = parallel_coord(sibling, axis);
245 int focused_pos = parallel_coord(focused, axis); 241 int focused_pos = parallel_coord(focused, axis);
@@ -277,8 +273,8 @@ static void resize_tiled(struct sway_container *parent, int amount,
277 enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ? 273 enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
278 WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM; 274 WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM;
279 275
280 for (int i = 0; i < parent->parent->children->length; i++) { 276 for (int i = 0; i < siblings->length; i++) {
281 struct sway_container *sibling = parent->parent->children->items[i]; 277 struct sway_container *sibling = siblings->items[i];
282 278
283 int sibling_pos = parallel_coord(sibling, axis); 279 int sibling_pos = parallel_coord(sibling, axis);
284 int focused_pos = parallel_coord(focused, axis); 280 int focused_pos = parallel_coord(focused, axis);
@@ -316,7 +312,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
316 } 312 }
317 } 313 }
318 314
319 arrange_windows(parent->parent); 315 if (parent->parent) {
316 arrange_container(parent->parent);
317 } else {
318 arrange_workspace(parent->workspace);
319 }
320} 320}
321 321
322void container_resize_tiled(struct sway_container *parent, 322void container_resize_tiled(struct sway_container *parent,
@@ -346,7 +346,7 @@ void container_resize_tiled(struct sway_container *parent,
346 */ 346 */
347static struct cmd_results *resize_adjust_floating(enum resize_axis axis, 347static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
348 struct resize_amount *amount) { 348 struct resize_amount *amount) {
349 struct sway_container *con = config->handler_context.current_container; 349 struct sway_container *con = config->handler_context.container;
350 int grow_width = 0, grow_height = 0; 350 int grow_width = 0, grow_height = 0;
351 switch (axis) { 351 switch (axis) {
352 case RESIZE_AXIS_HORIZONTAL: 352 case RESIZE_AXIS_HORIZONTAL:
@@ -400,15 +400,15 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
400 con->width += grow_width; 400 con->width += grow_width;
401 con->height += grow_height; 401 con->height += grow_height;
402 402
403 if (con->type == C_VIEW) { 403 if (con->view) {
404 struct sway_view *view = con->sway_view; 404 struct sway_view *view = con->view;
405 view->x += grow_x; 405 view->x += grow_x;
406 view->y += grow_y; 406 view->y += grow_y;
407 view->width += grow_width; 407 view->width += grow_width;
408 view->height += grow_height; 408 view->height += grow_height;
409 } 409 }
410 410
411 arrange_windows(con); 411 arrange_container(con);
412 412
413 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 413 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
414} 414}
@@ -418,7 +418,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
418 */ 418 */
419static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, 419static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
420 struct resize_amount *amount) { 420 struct resize_amount *amount) {
421 struct sway_container *current = config->handler_context.current_container; 421 struct sway_container *current = config->handler_context.container;
422 422
423 if (amount->unit == RESIZE_UNIT_DEFAULT) { 423 if (amount->unit == RESIZE_UNIT_DEFAULT) {
424 amount->unit = RESIZE_UNIT_PPT; 424 amount->unit = RESIZE_UNIT_PPT;
@@ -456,13 +456,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
456 width->unit == RESIZE_UNIT_DEFAULT) { 456 width->unit == RESIZE_UNIT_DEFAULT) {
457 // Convert to px 457 // Convert to px
458 struct sway_container *parent = con->parent; 458 struct sway_container *parent = con->parent;
459 while (parent->type >= C_WORKSPACE && parent->layout != L_HORIZ) { 459 while (parent && parent->layout != L_HORIZ) {
460 parent = parent->parent; 460 parent = parent->parent;
461 } 461 }
462 if (parent->type >= C_WORKSPACE) { 462 if (parent) {
463 width->amount = parent->width * width->amount / 100; 463 width->amount = parent->width * width->amount / 100;
464 width->unit = RESIZE_UNIT_PX; 464 } else {
465 width->amount = con->workspace->width * width->amount / 100;
465 } 466 }
467 width->unit = RESIZE_UNIT_PX;
466 } 468 }
467 if (width->unit == RESIZE_UNIT_PX) { 469 if (width->unit == RESIZE_UNIT_PX) {
468 resize_tiled(con, width->amount - con->width, 470 resize_tiled(con, width->amount - con->width,
@@ -475,13 +477,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
475 height->unit == RESIZE_UNIT_DEFAULT) { 477 height->unit == RESIZE_UNIT_DEFAULT) {
476 // Convert to px 478 // Convert to px
477 struct sway_container *parent = con->parent; 479 struct sway_container *parent = con->parent;
478 while (parent->type >= C_WORKSPACE && parent->layout != L_VERT) { 480 while (parent && parent->layout != L_VERT) {
479 parent = parent->parent; 481 parent = parent->parent;
480 } 482 }
481 if (parent->type >= C_WORKSPACE) { 483 if (parent) {
482 height->amount = parent->height * height->amount / 100; 484 height->amount = parent->height * height->amount / 100;
483 height->unit = RESIZE_UNIT_PX; 485 } else {
486 height->amount = con->workspace->height * height->amount / 100;
484 } 487 }
488 height->unit = RESIZE_UNIT_PX;
485 } 489 }
486 if (height->unit == RESIZE_UNIT_PX) { 490 if (height->unit == RESIZE_UNIT_PX) {
487 resize_tiled(con, height->amount - con->height, 491 resize_tiled(con, height->amount - con->height,
@@ -508,15 +512,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
508 con->width = width->amount; 512 con->width = width->amount;
509 con->height = height->amount; 513 con->height = height->amount;
510 514
511 if (con->type == C_VIEW) { 515 if (con->view) {
512 struct sway_view *view = con->sway_view; 516 struct sway_view *view = con->view;
513 view->x -= grow_width / 2; 517 view->x -= grow_width / 2;
514 view->y -= grow_height / 2; 518 view->y -= grow_height / 2;
515 view->width += grow_width; 519 view->width += grow_width;
516 view->height += grow_height; 520 view->height += grow_height;
517 } 521 }
518 522
519 arrange_windows(con); 523 arrange_container(con);
520 524
521 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 525 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
522} 526}
@@ -555,7 +559,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
555 } 559 }
556 560
557 // If 0, don't resize that dimension 561 // If 0, don't resize that dimension
558 struct sway_container *con = config->handler_context.current_container; 562 struct sway_container *con = config->handler_context.container;
559 if (width.amount <= 0) { 563 if (width.amount <= 0) {
560 width.amount = con->width; 564 width.amount = con->width;
561 } 565 }
@@ -624,7 +628,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
624 first_amount.amount *= multiplier; 628 first_amount.amount *= multiplier;
625 second_amount.amount *= multiplier; 629 second_amount.amount *= multiplier;
626 630
627 struct sway_container *con = config->handler_context.current_container; 631 struct sway_container *con = config->handler_context.container;
628 if (container_is_floating(con)) { 632 if (container_is_floating(con)) {
629 // Floating containers can only resize in px. Choose an amount which 633 // Floating containers can only resize in px. Choose an amount which
630 // uses px, with fallback to an amount that specified no unit. 634 // uses px, with fallback to an amount that specified no unit.
@@ -657,14 +661,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
657} 661}
658 662
659struct cmd_results *cmd_resize(int argc, char **argv) { 663struct cmd_results *cmd_resize(int argc, char **argv) {
660 struct sway_container *current = config->handler_context.current_container; 664 struct sway_container *current = config->handler_context.container;
661 if (!current) { 665 if (!current) {
662 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); 666 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
663 } 667 }
664 if (current->type != C_VIEW && current->type != C_CONTAINER) {
665 return cmd_results_new(CMD_INVALID, "resize",
666 "Can only resize views/containers");
667 }
668 668
669 struct cmd_results *error; 669 struct cmd_results *error;
670 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 670 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
index 7da20015..d8bae615 100644
--- a/sway/commands/scratchpad.c
+++ b/sway/commands/scratchpad.c
@@ -9,36 +9,34 @@
9 9
10static void scratchpad_toggle_auto(void) { 10static void scratchpad_toggle_auto(void) {
11 struct sway_seat *seat = input_manager_current_seat(input_manager); 11 struct sway_seat *seat = input_manager_current_seat(input_manager);
12 struct sway_container *focus = seat_get_focus(seat); 12 struct sway_container *focus = seat_get_focused_container(seat);
13 struct sway_container *ws = focus->type == C_WORKSPACE ? 13 struct sway_workspace *ws = seat_get_focused_workspace(seat);
14 focus : container_parent(focus, C_WORKSPACE);
15 14
16 // If the focus is in a floating split container, 15 // If the focus is in a floating split container,
17 // operate on the split container instead of the child. 16 // operate on the split container instead of the child.
18 if (container_is_floating_or_child(focus)) { 17 if (focus && container_is_floating_or_child(focus)) {
19 while (focus->parent->type != C_WORKSPACE) { 18 while (focus->parent) {
20 focus = focus->parent; 19 focus = focus->parent;
21 } 20 }
22 } 21 }
23 22
24
25 // Check if the currently focused window is a scratchpad window and should 23 // Check if the currently focused window is a scratchpad window and should
26 // be hidden again. 24 // be hidden again.
27 if (focus->scratchpad) { 25 if (focus && focus->scratchpad) {
28 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", 26 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s",
29 focus->name); 27 focus->title);
30 root_scratchpad_hide(focus); 28 root_scratchpad_hide(focus);
31 return; 29 return;
32 } 30 }
33 31
34 // Check if there is an unfocused scratchpad window on the current workspace 32 // Check if there is an unfocused scratchpad window on the current workspace
35 // and focus it. 33 // and focus it.
36 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 34 for (int i = 0; i < ws->floating->length; ++i) {
37 struct sway_container *floater = ws->sway_workspace->floating->items[i]; 35 struct sway_container *floater = ws->floating->items[i];
38 if (floater->scratchpad && focus != floater) { 36 if (floater->scratchpad && focus != floater) {
39 wlr_log(WLR_DEBUG, 37 wlr_log(WLR_DEBUG,
40 "Focusing other scratchpad window (%s) in this workspace", 38 "Focusing other scratchpad window (%s) in this workspace",
41 floater->name); 39 floater->title);
42 root_scratchpad_show(floater); 40 root_scratchpad_show(floater);
43 return; 41 return;
44 } 42 }
@@ -46,25 +44,23 @@ static void scratchpad_toggle_auto(void) {
46 44
47 // Check if there is a visible scratchpad window on another workspace. 45 // Check if there is a visible scratchpad window on another workspace.
48 // In this case we move it to the current workspace. 46 // In this case we move it to the current workspace.
49 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 47 for (int i = 0; i < root->scratchpad->length; ++i) {
50 struct sway_container *con = 48 struct sway_container *con = root->scratchpad->items[i];
51 root_container.sway_root->scratchpad->items[i];
52 if (con->parent) { 49 if (con->parent) {
53 wlr_log(WLR_DEBUG, 50 wlr_log(WLR_DEBUG,
54 "Moving a visible scratchpad window (%s) to this workspace", 51 "Moving a visible scratchpad window (%s) to this workspace",
55 con->name); 52 con->title);
56 root_scratchpad_show(con); 53 root_scratchpad_show(con);
57 return; 54 return;
58 } 55 }
59 } 56 }
60 57
61 // Take the container at the bottom of the scratchpad list 58 // Take the container at the bottom of the scratchpad list
62 if (!sway_assert(root_container.sway_root->scratchpad->length, 59 if (!sway_assert(root->scratchpad->length, "Scratchpad is empty")) {
63 "Scratchpad is empty")) {
64 return; 60 return;
65 } 61 }
66 struct sway_container *con = root_container.sway_root->scratchpad->items[0]; 62 struct sway_container *con = root->scratchpad->items[0];
67 wlr_log(WLR_DEBUG, "Showing %s from list", con->name); 63 wlr_log(WLR_DEBUG, "Showing %s from list", con->title);
68 root_scratchpad_show(con); 64 root_scratchpad_show(con);
69} 65}
70 66
@@ -74,7 +70,7 @@ static void scratchpad_toggle_container(struct sway_container *con) {
74 } 70 }
75 71
76 // Check if it matches a currently visible scratchpad window and hide it. 72 // Check if it matches a currently visible scratchpad window and hide it.
77 if (con->parent) { 73 if (con->workspace) {
78 root_scratchpad_hide(con); 74 root_scratchpad_hide(con);
79 return; 75 return;
80 } 76 }
@@ -91,18 +87,18 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
91 return cmd_results_new(CMD_INVALID, "scratchpad", 87 return cmd_results_new(CMD_INVALID, "scratchpad",
92 "Expected 'scratchpad show'"); 88 "Expected 'scratchpad show'");
93 } 89 }
94 if (!root_container.sway_root->scratchpad->length) { 90 if (!root->scratchpad->length) {
95 return cmd_results_new(CMD_INVALID, "scratchpad", 91 return cmd_results_new(CMD_INVALID, "scratchpad",
96 "Scratchpad is empty"); 92 "Scratchpad is empty");
97 } 93 }
98 94
99 if (config->handler_context.using_criteria) { 95 if (config->handler_context.using_criteria) {
100 struct sway_container *con = config->handler_context.current_container; 96 struct sway_container *con = config->handler_context.container;
101 97
102 // If the container is in a floating split container, 98 // If the container is in a floating split container,
103 // operate on the split container instead of the child. 99 // operate on the split container instead of the child.
104 if (container_is_floating_or_child(con)) { 100 if (container_is_floating_or_child(con)) {
105 while (con->parent->type != C_WORKSPACE) { 101 while (con->parent) {
106 con = con->parent; 102 con = con->parent;
107 } 103 }
108 } 104 }
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 4d0a22c7..cd6630e0 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -42,8 +42,8 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
43 } 43 }
44 // map absolute coords (0..1,0..1) to root container coords 44 // map absolute coords (0..1,0..1) to root container coords
45 float x = strtof(argv[1], NULL) / root_container.width; 45 float x = strtof(argv[1], NULL) / root->width;
46 float y = strtof(argv[2], NULL) / root_container.height; 46 float y = strtof(argv[2], NULL) / root->height;
47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); 47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
48 cursor_send_pointer_motion(cursor, 0, true); 48 cursor_send_pointer_motion(cursor, 0, true);
49 } else { 49 } else {
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 1844e917..d501584a 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -11,8 +11,8 @@
11#include "util.h" 11#include "util.h"
12 12
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 13static void rebuild_marks_iterator(struct sway_container *con, void *data) {
14 if (con->type == C_VIEW) { 14 if (con->view) {
15 view_update_marks_textures(con->sway_view); 15 view_update_marks_textures(con->view);
16 } 16 }
17} 17}
18 18
@@ -28,9 +28,9 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
28 root_for_each_container(rebuild_marks_iterator, NULL); 28 root_for_each_container(rebuild_marks_iterator, NULL);
29 } 29 }
30 30
31 for (int i = 0; i < root_container.children->length; ++i) { 31 for (int i = 0; i < root->outputs->length; ++i) {
32 struct sway_container *con = root_container.children->items[i]; 32 struct sway_output *output = root->outputs->items[i];
33 output_damage_whole(con->sway_output); 33 output_damage_whole(output);
34 } 34 }
35 35
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index 7d27e571..273905df 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
23 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
24 } 24 }
25 25
26 arrange_windows(&root_container); 26 arrange_root();
27 27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 29}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index a8eddf54..9a53f3d3 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -4,15 +4,21 @@
4#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h" 6#include "sway/tree/view.h"
7#include "sway/tree/workspace.h"
7#include "sway/input/input-manager.h" 8#include "sway/input/input-manager.h"
8#include "sway/input/seat.h" 9#include "sway/input/seat.h"
9#include "log.h" 10#include "log.h"
10 11
11static struct cmd_results *do_split(int layout) { 12static struct cmd_results *do_split(int layout) {
12 struct sway_container *con = config->handler_context.current_container; 13 struct sway_container *con = config->handler_context.container;
13 struct sway_container *parent = container_split(con, layout); 14 struct sway_workspace *ws = config->handler_context.workspace;
14 container_create_notify(parent); 15 if (con) {
15 arrange_windows(parent->parent); 16 container_split(con, layout);
17 } else {
18 workspace_split(ws, layout);
19 }
20
21 arrange_workspace(ws);
16 22
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18} 24}
@@ -29,10 +35,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
29 return do_split(L_HORIZ); 35 return do_split(L_HORIZ);
30 } else if (strcasecmp(argv[0], "t") == 0 || 36 } else if (strcasecmp(argv[0], "t") == 0 ||
31 strcasecmp(argv[0], "toggle") == 0) { 37 strcasecmp(argv[0], "toggle") == 0) {
32 struct sway_container *focused = 38 struct sway_container *focused = config->handler_context.container;
33 config->handler_context.current_container;
34 39
35 if (focused->parent->layout == L_VERT) { 40 if (focused && container_parent_layout(focused) == L_VERT) {
36 return do_split(L_HORIZ); 41 return do_split(L_HORIZ);
37 } else { 42 } else {
38 return do_split(L_VERT); 43 return do_split(L_VERT);
@@ -66,9 +71,9 @@ struct cmd_results *cmd_splitt(int argc, char **argv) {
66 return error; 71 return error;
67 } 72 }
68 73
69 struct sway_container *con = config->handler_context.current_container; 74 struct sway_container *con = config->handler_context.container;
70 75
71 if (con->parent->layout == L_VERT) { 76 if (con && container_parent_layout(con) == L_VERT) {
72 return do_split(L_HORIZ); 77 return do_split(L_HORIZ);
73 } else { 78 } else {
74 return do_split(L_VERT); 79 return do_split(L_VERT);
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index 8692e08d..7995cdd6 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -15,8 +15,7 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) { 15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = 18 struct sway_container *container = config->handler_context.container;
19 config->handler_context.current_container;
20 if (!container_is_floating(container)) { 19 if (!container_is_floating(container)) {
21 return cmd_results_new(CMD_FAILURE, "sticky", 20 return cmd_results_new(CMD_FAILURE, "sticky",
22 "Can't set sticky on a tiled container"); 21 "Can't set sticky on a tiled container");
@@ -37,20 +36,16 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
37 container->is_sticky = wants_sticky; 36 container->is_sticky = wants_sticky;
38 37
39 if (wants_sticky) { 38 if (wants_sticky) {
40 // move container to focused workspace 39 // move container to active workspace
41 struct sway_container *output = container_parent(container, C_OUTPUT); 40 struct sway_workspace *active_workspace =
42 struct sway_seat *seat = input_manager_current_seat(input_manager); 41 output_get_active_workspace(container->workspace->output);
43 struct sway_container *focus = seat_get_focus_inactive(seat, output); 42 if (container->workspace != active_workspace) {
44 struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); 43 struct sway_workspace *old_workspace = container->workspace;
45 struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); 44 container_detach(container);
46 if (current_workspace != focused_workspace) { 45 workspace_add_floating(active_workspace, container);
47 container_remove_child(container); 46 container_handle_fullscreen_reparent(container);
48 workspace_add_floating(focused_workspace, container); 47 arrange_workspace(active_workspace);
49 container_handle_fullscreen_reparent(container, current_workspace); 48 workspace_consider_destroy(old_workspace);
50 arrange_windows(focused_workspace);
51 if (!container_reap_empty(current_workspace)) {
52 arrange_windows(current_workspace);
53 }
54 } 49 }
55 } 50 }
56 51
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index f25c43a1..a0ffbda8 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -4,6 +4,7 @@
4#include "config.h" 4#include "config.h"
5#include "log.h" 5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/output.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
8#include "sway/tree/root.h" 9#include "sway/tree/root.h"
9#include "sway/tree/view.h" 10#include "sway/tree/view.h"
@@ -43,27 +44,28 @@ static void swap_focus(struct sway_container *con1,
43 struct sway_container *con2, struct sway_seat *seat, 44 struct sway_container *con2, struct sway_seat *seat,
44 struct sway_container *focus) { 45 struct sway_container *focus) {
45 if (focus == con1 || focus == con2) { 46 if (focus == con1 || focus == con2) {
46 struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); 47 struct sway_workspace *ws1 = con1->workspace;
47 struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); 48 struct sway_workspace *ws2 = con2->workspace;
48 if (focus == con1 && (con2->parent->layout == L_TABBED 49 enum sway_container_layout layout1 = container_parent_layout(con1);
49 || con2->parent->layout == L_STACKED)) { 50 enum sway_container_layout layout2 = container_parent_layout(con2);
51 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
50 if (workspace_is_visible(ws2)) { 52 if (workspace_is_visible(ws2)) {
51 seat_set_focus_warp(seat, con2, false, true); 53 seat_set_focus_warp(seat, &con2->node, false, true);
52 } 54 }
53 seat_set_focus(seat, ws1 != ws2 ? con2 : con1); 55 seat_set_focus(seat, ws1 != ws2 ? &con2->node : &con1->node);
54 } else if (focus == con2 && (con1->parent->layout == L_TABBED 56 } else if (focus == con2 && (layout1 == L_TABBED
55 || con1->parent->layout == L_STACKED)) { 57 || layout1 == L_STACKED)) {
56 if (workspace_is_visible(ws1)) { 58 if (workspace_is_visible(ws1)) {
57 seat_set_focus_warp(seat, con1, false, true); 59 seat_set_focus_warp(seat, &con1->node, false, true);
58 } 60 }
59 seat_set_focus(seat, ws1 != ws2 ? con1 : con2); 61 seat_set_focus(seat, ws1 != ws2 ? &con1->node : &con2->node);
60 } else if (ws1 != ws2) { 62 } else if (ws1 != ws2) {
61 seat_set_focus(seat, focus == con1 ? con2 : con1); 63 seat_set_focus(seat, focus == con1 ? &con2->node : &con1->node);
62 } else { 64 } else {
63 seat_set_focus(seat, focus); 65 seat_set_focus(seat, &focus->node);
64 } 66 }
65 } else { 67 } else {
66 seat_set_focus(seat, focus); 68 seat_set_focus(seat, &focus->node);
67 } 69 }
68} 70}
69 71
@@ -72,10 +74,6 @@ static void container_swap(struct sway_container *con1,
72 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { 74 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
73 return; 75 return;
74 } 76 }
75 if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER,
76 "Can only swap containers and views")) {
77 return;
78 }
79 if (!sway_assert(!container_has_ancestor(con1, con2) 77 if (!sway_assert(!container_has_ancestor(con1, con2)
80 && !container_has_ancestor(con2, con1), 78 && !container_has_ancestor(con2, con1),
81 "Cannot swap ancestor and descendant")) { 79 "Cannot swap ancestor and descendant")) {
@@ -87,10 +85,11 @@ static void container_swap(struct sway_container *con1,
87 return; 85 return;
88 } 86 }
89 87
90 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); 88 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu",
89 con1->node.id, con2->node.id);
91 90
92 int fs1 = con1->is_fullscreen; 91 bool fs1 = con1->is_fullscreen;
93 int fs2 = con2->is_fullscreen; 92 bool fs2 = con2->is_fullscreen;
94 if (fs1) { 93 if (fs1) {
95 container_set_fullscreen(con1, false); 94 container_set_fullscreen(con1, false);
96 } 95 }
@@ -99,13 +98,11 @@ static void container_swap(struct sway_container *con1,
99 } 98 }
100 99
101 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 100 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
102 struct sway_container *focus = seat_get_focus(seat); 101 struct sway_container *focus = seat_get_focused_container(seat);
103 struct sway_container *vis1 = container_parent( 102 struct sway_workspace *vis1 =
104 seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), 103 output_get_active_workspace(con1->workspace->output);
105 C_WORKSPACE); 104 struct sway_workspace *vis2 =
106 struct sway_container *vis2 = container_parent( 105 output_get_active_workspace(con2->workspace->output);
107 seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
108 C_WORKSPACE);
109 106
110 char *stored_prev_name = NULL; 107 char *stored_prev_name = NULL;
111 if (prev_workspace_name) { 108 if (prev_workspace_name) {
@@ -115,10 +112,10 @@ static void container_swap(struct sway_container *con1,
115 swap_places(con1, con2); 112 swap_places(con1, con2);
116 113
117 if (!workspace_is_visible(vis1)) { 114 if (!workspace_is_visible(vis1)) {
118 seat_set_focus(seat, seat_get_focus_inactive(seat, vis1)); 115 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
119 } 116 }
120 if (!workspace_is_visible(vis2)) { 117 if (!workspace_is_visible(vis2)) {
121 seat_set_focus(seat, seat_get_focus_inactive(seat, vis2)); 118 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
122 } 119 }
123 120
124 swap_focus(con1, con2, seat, focus); 121 swap_focus(con1, con2, seat, focus);
@@ -137,23 +134,22 @@ static void container_swap(struct sway_container *con1,
137} 134}
138 135
139static bool test_con_id(struct sway_container *container, void *con_id) { 136static bool test_con_id(struct sway_container *container, void *con_id) {
140 return container->id == (size_t)con_id; 137 return container->node.id == (size_t)con_id;
141} 138}
142 139
143static bool test_id(struct sway_container *container, void *id) { 140static bool test_id(struct sway_container *container, void *id) {
144#ifdef HAVE_XWAYLAND 141#ifdef HAVE_XWAYLAND
145 xcb_window_t *wid = id; 142 xcb_window_t *wid = id;
146 return (container->type == C_VIEW 143 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
147 && container->sway_view->type == SWAY_VIEW_XWAYLAND 144 && container->view->wlr_xwayland_surface->window_id == *wid);
148 && container->sway_view->wlr_xwayland_surface->window_id == *wid);
149#else 145#else
150 return false; 146 return false;
151#endif 147#endif
152} 148}
153 149
154static bool test_mark(struct sway_container *container, void *mark) { 150static bool test_mark(struct sway_container *container, void *mark) {
155 if (container->type == C_VIEW && container->sway_view->marks->length) { 151 if (container->view && container->view->marks->length) {
156 return !list_seq_find(container->sway_view->marks, 152 return !list_seq_find(container->view->marks,
157 (int (*)(const void *, const void *))strcmp, mark); 153 (int (*)(const void *, const void *))strcmp, mark);
158 } 154 }
159 return false; 155 return false;
@@ -169,7 +165,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
169 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); 165 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
170 } 166 }
171 167
172 struct sway_container *current = config->handler_context.current_container; 168 struct sway_container *current = config->handler_context.container;
173 struct sway_container *other; 169 struct sway_container *other;
174 170
175 char *value = join_args(argv + 3, argc - 3); 171 char *value = join_args(argv + 3, argc - 3);
@@ -191,7 +187,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
191 if (!other) { 187 if (!other) {
192 error = cmd_results_new(CMD_FAILURE, "swap", 188 error = cmd_results_new(CMD_FAILURE, "swap",
193 "Failed to find %s '%s'", argv[2], value); 189 "Failed to find %s '%s'", argv[2], value);
194 } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) { 190 } else if (!current) {
195 error = cmd_results_new(CMD_FAILURE, "swap", 191 error = cmd_results_new(CMD_FAILURE, "swap",
196 "Can only swap with containers and views"); 192 "Can only swap with containers and views");
197 } else if (container_has_ancestor(current, other) 193 } else if (container_has_ancestor(current, other)
@@ -211,9 +207,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
211 207
212 container_swap(current, other); 208 container_swap(current, other);
213 209
214 arrange_windows(current->parent); 210 arrange_node(node_get_parent(&current->node));
215 if (other->parent != current->parent) { 211 if (node_get_parent(&other->node) != node_get_parent(&current->node)) {
216 arrange_windows(other->parent); 212 arrange_node(node_get_parent(&other->node));
217 } 213 }
218 214
219 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 215 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 3d1c578c..c9ffe8fa 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -11,13 +11,12 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
11 if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { 11 if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct sway_container *container = 14 struct sway_container *container = config->handler_context.container;
15 config->handler_context.current_container; 15 if (!container->view) {
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "title_format", 16 return cmd_results_new(CMD_INVALID, "title_format",
18 "Only views can have a title_format"); 17 "Only views can have a title_format");
19 } 18 }
20 struct sway_view *view = container->sway_view; 19 struct sway_view *view = container->view;
21 char *format = join_args(argv, argc); 20 char *format = join_args(argv, argc);
22 if (view->title_format) { 21 if (view->title_format) {
23 free(view->title_format); 22 free(view->title_format);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index 62127c97..c6251dc8 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -9,9 +9,9 @@
9#include "stringop.h" 9#include "stringop.h"
10 10
11static void remove_all_marks_iterator(struct sway_container *con, void *data) { 11static void remove_all_marks_iterator(struct sway_container *con, void *data) {
12 if (con->type == C_VIEW) { 12 if (con->view) {
13 view_clear_marks(con->sway_view); 13 view_clear_marks(con->view);
14 view_update_marks_textures(con->sway_view); 14 view_update_marks_textures(con->view);
15 } 15 }
16} 16}
17 17
@@ -24,13 +24,12 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
24 // Determine the view 24 // Determine the view
25 struct sway_view *view = NULL; 25 struct sway_view *view = NULL;
26 if (config->handler_context.using_criteria) { 26 if (config->handler_context.using_criteria) {
27 struct sway_container *container = 27 struct sway_container *container = config->handler_context.container;
28 config->handler_context.current_container; 28 if (!container->view) {
29 if (container->type != C_VIEW) {
30 return cmd_results_new(CMD_INVALID, "unmark", 29 return cmd_results_new(CMD_INVALID, "unmark",
31 "Only views can have marks"); 30 "Only views can have marks");
32 } 31 }
33 view = container->sway_view; 32 view = container->view;
34 } 33 }
35 34
36 // Determine the mark 35 // Determine the mark
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c
index bccb33fe..53c37d4d 100644
--- a/sway/commands/urgent.c
+++ b/sway/commands/urgent.c
@@ -11,13 +11,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) {
11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { 11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct sway_container *container = 14 struct sway_container *container = config->handler_context.container;
15 config->handler_context.current_container; 15 if (!container->view) {
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "urgent", 16 return cmd_results_new(CMD_INVALID, "urgent",
18 "Only views can be urgent"); 17 "Only views can be urgent");
19 } 18 }
20 struct sway_view *view = container->sway_view; 19 struct sway_view *view = container->view;
21 20
22 if (strcmp(argv[0], "allow") == 0) { 21 if (strcmp(argv[0], "allow") == 0) {
23 view->allow_request_urgent = true; 22 view->allow_request_urgent = true;
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index ceb4cd6e..f026a39d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -58,7 +58,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
58 } 58 }
59 59
60 60
61 struct sway_container *ws = NULL; 61 struct sway_workspace *ws = NULL;
62 if (strcasecmp(argv[0], "number") == 0) { 62 if (strcasecmp(argv[0], "number") == 0) {
63 if (argc < 2) { 63 if (argc < 2) {
64 return cmd_results_new(CMD_INVALID, "workspace", 64 return cmd_results_new(CMD_INVALID, "workspace",