aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-04-02 13:49:37 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2018-04-06 09:43:52 -0400
commit57954a2b24f1e211c3b8811fb898ef4e076cb098 (patch)
treeb9977589b4b84811e31c0edec636252d04603ec7
parentAvoid arranging windows while reloading config (diff)
downloadsway-57954a2b24f1e211c3b8811fb898ef4e076cb098.tar.gz
sway-57954a2b24f1e211c3b8811fb898ef4e076cb098.tar.zst
sway-57954a2b24f1e211c3b8811fb898ef4e076cb098.zip
Implement move [left|right|up|down]
The exact semantics of this command are complicated. I'll describe each test scenario as s-expressions. Everything assumes L_HORIZ if not specified, but if you rotate everything 90 degrees the same test cases hold. ``` (container (view a) (view b focus) (view c)) -> move left (container (view b focus) (view a) (view c)) (container (view a) (view b focus) (view c)) -> move right (container (view a) (view c) (view b focus)) (container L_VERT (view a)) (container L_HORIZ (view b) (view c focus)) -> move up (container L_VERT (view a) (view c focus)) (container L_HORIZ (view b)) (workspace (view a) (view b focus) (view c)) -> move up (workspace [split direction flipped] (view b focus) (container (view a) (view c))) (workspace (view a) (view b focus) (view c)) -> move down (workspace [split direction flipped] (container (view a) (view c)) (view b focus))) Note: outputs use wlr_output_layout instead of assuming that i+/-1 is the next output in the move direction. (root (output X11-1 (workspace 1)) (output X11-2 (workspace 1 (view a focus) (view b))))) -> move left (root (output X11-1 (workspace 1 (view a focus))) (output X11-2 (workspace 1 (view b))))) (root (output X11-1 (workspace 1 (container (view a) (view b))) (output X11-2 (workspace 1 (view c focus))))) -> move left (root (output X11-1 (workspace 1 (container (view a) (view b)) (view c focus))) (output X11-2 (workspace 1))) ```
-rw-r--r--include/sway/tree/container.h7
-rw-r--r--sway/commands/move.c4
-rw-r--r--sway/tree/container.c46
-rw-r--r--sway/tree/layout.c294
4 files changed, 306 insertions, 45 deletions
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 4c60530f..2a8b8aba 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -182,4 +182,11 @@ void container_create_notify(struct sway_container *container);
182 182
183void container_damage_whole(struct sway_container *container); 183void container_damage_whole(struct sway_container *container);
184 184
185bool container_reap_empty(struct sway_container *con);
186
187struct sway_container *container_reap_empty_recursive(
188 struct sway_container *con);
189
190struct sway_container *container_flatten(struct sway_container *container);
191
185#endif 192#endif
diff --git a/sway/commands/move.c b/sway/commands/move.c
index c954ab94..15a5ebc4 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -82,6 +82,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
82 config->handler_context.seat, ws); 82 config->handler_context.seat, ws);
83 container_move_to(current, focus); 83 container_move_to(current, focus);
84 seat_set_focus(config->handler_context.seat, old_parent); 84 seat_set_focus(config->handler_context.seat, old_parent);
85 container_reap_empty(old_parent);
86 container_reap_empty(focus->parent);
85 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 87 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
86 } else if (strcasecmp(argv[1], "to") == 0 88 } else if (strcasecmp(argv[1], "to") == 0
87 && strcasecmp(argv[2], "output") == 0) { 89 && strcasecmp(argv[2], "output") == 0) {
@@ -109,6 +111,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
109 struct sway_container *old_parent = current->parent; 111 struct sway_container *old_parent = current->parent;
110 container_move_to(current, focus); 112 container_move_to(current, focus);
111 seat_set_focus(config->handler_context.seat, old_parent); 113 seat_set_focus(config->handler_context.seat, old_parent);
114 container_reap_empty(old_parent);
115 container_reap_empty(focus->parent);
112 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 116 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
113 } 117 }
114 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 118 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index ab8363bc..ea1c93bb 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -208,7 +208,7 @@ static void container_root_finish(struct sway_container *con) {
208 wlr_log(L_ERROR, "TODO: destroy the root container"); 208 wlr_log(L_ERROR, "TODO: destroy the root container");
209} 209}
210 210
211static bool container_reap_empty(struct sway_container *con) { 211bool container_reap_empty(struct sway_container *con) {
212 switch (con->type) { 212 switch (con->type) {
213 case C_ROOT: 213 case C_ROOT:
214 case C_OUTPUT: 214 case C_OUTPUT:
@@ -225,14 +225,6 @@ static bool container_reap_empty(struct sway_container *con) {
225 if (con->children->length == 0) { 225 if (con->children->length == 0) {
226 _container_destroy(con); 226 _container_destroy(con);
227 return true; 227 return true;
228 } else if (con->children->length == 1) {
229 struct sway_container *child = con->children->items[0];
230 if (child->type == C_CONTAINER) {
231 container_remove_child(child);
232 container_replace_child(con, child);
233 _container_destroy(con);
234 return true;
235 }
236 } 228 }
237 case C_VIEW: 229 case C_VIEW:
238 break; 230 break;
@@ -245,6 +237,29 @@ static bool container_reap_empty(struct sway_container *con) {
245 return false; 237 return false;
246} 238}
247 239
240struct sway_container *container_reap_empty_recursive(
241 struct sway_container *con) {
242 while (con) {
243 struct sway_container *next = con->parent;
244 if (!container_reap_empty(con)) {
245 break;
246 }
247 con = next;
248 }
249 return con;
250}
251
252struct sway_container *container_flatten(struct sway_container *container) {
253 while (container->type == C_CONTAINER && container->children->length == 1) {
254 struct sway_container *child = container->children->items[0];
255 struct sway_container *parent = container->parent;
256 container_replace_child(container, child);
257 container_destroy(container);
258 container = parent;
259 }
260 return container;
261}
262
248struct sway_container *container_destroy(struct sway_container *con) { 263struct sway_container *container_destroy(struct sway_container *con) {
249 if (con == NULL) { 264 if (con == NULL) {
250 return NULL; 265 return NULL;
@@ -283,18 +298,7 @@ struct sway_container *container_destroy(struct sway_container *con) {
283 break; 298 break;
284 } 299 }
285 300
286 struct sway_container *tmp = parent; 301 return container_reap_empty_recursive(parent);
287 while (parent) {
288 tmp = parent->parent;
289
290 if (!container_reap_empty(parent)) {
291 break;
292 }
293
294 parent = tmp;
295 }
296
297 return tmp;
298} 302}
299 303
300static void container_close_func(struct sway_container *container, void *data) { 304static void container_close_func(struct sway_container *container, void *data) {
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 343f349a..a060cb85 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -100,13 +100,31 @@ static int index_child(const struct sway_container *child) {
100 return i; 100 return i;
101} 101}
102 102
103void container_insert_child(struct sway_container *parent,
104 struct sway_container *child, int i) {
105 struct sway_container *old_parent = NULL;
106 list_insert(parent->children, i, child);
107 child->parent = parent;
108 if (old_parent && old_parent != parent) {
109 wl_signal_emit(&child->events.reparent, old_parent);
110 }
111}
112
103struct sway_container *container_add_sibling(struct sway_container *fixed, 113struct sway_container *container_add_sibling(struct sway_container *fixed,
104 struct sway_container *active) { 114 struct sway_container *active) {
105 // TODO handle floating 115 // TODO handle floating
116 struct sway_container *old_parent = NULL;
117 if (active->parent) {
118 old_parent = active->parent;
119 container_remove_child(active);
120 }
106 struct sway_container *parent = fixed->parent; 121 struct sway_container *parent = fixed->parent;
107 int i = index_child(fixed); 122 int i = index_child(fixed);
108 list_insert(parent->children, i + 1, active); 123 list_insert(parent->children, i + 1, active);
109 active->parent = parent; 124 active->parent = parent;
125 if (old_parent && old_parent != parent) {
126 wl_signal_emit(&active->events.reparent, old_parent);
127 }
110 return active->parent; 128 return active->parent;
111} 129}
112 130
@@ -166,9 +184,253 @@ void container_move_to(struct sway_container *container,
166 arrange_windows(new_parent, -1, -1); 184 arrange_windows(new_parent, -1, -1);
167} 185}
168 186
187static bool sway_dir_to_wlr(enum movement_direction dir,
188 enum wlr_direction *out) {
189 switch (dir) {
190 case MOVE_UP:
191 *out = WLR_DIRECTION_UP;
192 break;
193 case MOVE_DOWN:
194 *out = WLR_DIRECTION_DOWN;
195 break;
196 case MOVE_LEFT:
197 *out = WLR_DIRECTION_LEFT;
198 break;
199 case MOVE_RIGHT:
200 *out = WLR_DIRECTION_RIGHT;
201 break;
202 default:
203 return false;
204 }
205
206 return true;
207}
208
209static bool is_parallel(enum sway_container_layout layout,
210 enum movement_direction dir) {
211 switch (layout) {
212 case L_TABBED:
213 case L_STACKED:
214 case L_HORIZ:
215 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
216 case L_VERT:
217 return dir == MOVE_UP || dir == MOVE_DOWN;
218 default:
219 return false;
220 }
221}
222
223static enum movement_direction invert_movement(enum movement_direction dir) {
224 switch (dir) {
225 case MOVE_LEFT:
226 return MOVE_RIGHT;
227 case MOVE_RIGHT:
228 return MOVE_LEFT;
229 case MOVE_UP:
230 return MOVE_DOWN;
231 case MOVE_DOWN:
232 return MOVE_LEFT;
233 default:
234 sway_assert(0, "This function expects left|right|up|down");
235 return MOVE_LEFT;
236 }
237}
238
239/* Gets the index of the most extreme member based on the movement offset */
240static int container_limit(struct sway_container *container, int offs) {
241 if (container->children->length == 0) {
242 return 0;
243 }
244 return offs < 0 ? 0 : container->children->length - 1;
245}
246
247static int move_offs(enum movement_direction move_dir) {
248 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
249}
250
251static void workspace_rejigger(struct sway_container *ws,
252 struct sway_container *child, enum movement_direction move_dir) {
253 struct sway_container *original_parent = child->parent;
254 struct sway_container *new_parent =
255 container_split(ws, ws->layout);
256
257 container_remove_child(child);
258 for (int i = 0; i < ws->children->length; ++i) {
259 struct sway_container *_child = ws->children->items[i];
260 container_move_to(new_parent, _child);
261 }
262
263 int index = move_offs(move_dir);
264 container_insert_child(ws, child, index < 0 ? 0 : 1);
265 container_set_layout(ws,
266 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
267
268 container_flatten(ws);
269 container_reap_empty_recursive(original_parent);
270 arrange_windows(ws, -1, -1);
271}
272
169void container_move(struct sway_container *container, 273void container_move(struct sway_container *container,
170 enum movement_direction dir, int move_amt) { 274 enum movement_direction move_dir, int move_amt) {
171 // TODO 275 if (!sway_assert(
276 container->type != C_CONTAINER || container->type != C_VIEW,
277 "Can only move containers and views")) {
278 return;
279 }
280 int offs = move_offs(move_dir);
281
282 struct sway_container *sibling = NULL;
283 struct sway_container *current = container;
284 struct sway_container *parent = current->parent;
285
286 if (parent != container_flatten(parent)) {
287 // Special case: we were the last one in this container, so flatten it
288 // and leave
289 return;
290 }
291
292 while (!sibling) {
293 if (current->type == C_ROOT) {
294 return;
295 }
296
297 parent = current->parent;
298 wlr_log(L_DEBUG, "Visiting %p %s '%s'", current,
299 container_type_to_str(current->type), current->name);
300
301 int index = index_child(current);
302 int limit = container_limit(parent, offs);
303
304 switch (current->type) {
305 case C_OUTPUT: {
306 enum wlr_direction wlr_dir;
307 sway_dir_to_wlr(move_dir, &wlr_dir);
308 double ref_x = current->x + current->width / 2;
309 double ref_y = current->y + current->height / 2;
310 ref_x += current->sway_output->wlr_output->lx;
311 ref_y += current->sway_output->wlr_output->ly;
312 struct wlr_output *next = wlr_output_layout_adjacent_output(
313 root_container.sway_root->output_layout, wlr_dir,
314 current->sway_output->wlr_output,
315 current->x, current->y);
316 if (!next) {
317 wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go");
318 return;
319 }
320 struct sway_output *next_output = next->data;
321 current = next_output->swayc;
322 wlr_log(L_DEBUG, "Selected next output (%s)", current->name);
323 // Select workspace and get outta here
324 current = seat_get_focus_inactive(
325 config->handler_context.seat, current);
326 if (current->type != C_WORKSPACE) {
327 current = container_parent(current, C_WORKSPACE);
328 }
329 sibling = current;
330 break;
331 }
332 case C_WORKSPACE:
333 if (!is_parallel(current->layout, move_dir)) {
334 // Special case
335 wlr_log(L_DEBUG, "Rejiggering the workspace");
336 workspace_rejigger(current, container, move_dir);
337 return;
338 } else {
339 wlr_log(L_DEBUG, "Selecting output");
340 current = current->parent;
341 }
342 break;
343 case C_CONTAINER:
344 case C_VIEW:
345 if (is_parallel(parent->layout, move_dir)) {
346 if (index == limit) {
347 if (current->parent == container->parent) {
348 wlr_log(L_DEBUG, "Hit limit, selecting parent");
349 current = current->parent;
350 } else {
351 wlr_log(L_DEBUG, "Hit limit, "
352 "promoting descendant to sibling");
353 // Special case
354 struct sway_container *old_parent = container->parent;
355 container_remove_child(container);
356 container_insert_child(current->parent, container,
357 index + (offs < 0 ? 0 : 1));
358 container->width = container->height = 0;
359 arrange_windows(current->parent, -1, -1);
360 arrange_windows(old_parent, -1, -1);
361 return;
362 }
363 } else {
364 wlr_log(L_DEBUG, "Selecting sibling");
365 sibling = parent->children->items[index + offs];
366 }
367 } else {
368 wlr_log(L_DEBUG, "Moving up to find a parallel container");
369 current = current->parent;
370 }
371 break;
372 default:
373 sway_assert(0, "Not expecting to see container of type %s here",
374 container_type_to_str(current->type));
375 return;
376 }
377 }
378
379 // Part two: move stuff around
380 int index = index_child(container);
381 struct sway_container *old_parent = container->parent;
382
383 switch (sibling->type) {
384 case C_VIEW:
385 if (sibling->parent == container->parent) {
386 wlr_log(L_DEBUG, "Swapping siblings");
387 sibling->parent->children->items[index + offs] = container;
388 sibling->parent->children->items[index] = sibling;
389 arrange_windows(sibling->parent, -1, -1);
390 } else {
391 wlr_log(L_DEBUG, "Promoting to sibling of cousin");
392 container_remove_child(container);
393 container_insert_child(sibling->parent, container,
394 index_child(sibling) + (offs > 0 ? 0 : 1));
395 container->width = container->height = 0;
396 arrange_windows(sibling->parent, -1, -1);
397 arrange_windows(old_parent, -1, -1);
398 }
399 break;
400 case C_WORKSPACE: // Note: only in the case of moving between outputs
401 case C_CONTAINER:
402 if (is_parallel(sibling->layout, move_dir)) {
403 int limit = container_limit(sibling, move_dir);
404 wlr_log(L_DEBUG, "Reparenting container (paralell)");
405 limit = limit != 0 ? limit + 1 : limit; // Convert to index
406 wlr_log(L_DEBUG, "Reparenting container (paralell) %d", limit);
407 container_remove_child(container);
408 container_insert_child(sibling, container, limit);
409 container->width = container->height = 0;
410 arrange_windows(sibling, -1, -1);
411 arrange_windows(old_parent, -1, -1);
412 } else {
413 wlr_log(L_DEBUG, "Reparenting container (perpendicular)");
414 container_remove_child(container);
415 struct sway_container *focus_inactive = seat_get_focus_inactive(
416 config->handler_context.seat, sibling);
417 if (focus_inactive) {
418 container_add_sibling(focus_inactive, container);
419 } else if (sibling->children->length) {
420 container_add_sibling(sibling->children->items[0], container);
421 } else {
422 container_add_child(sibling, container);
423 }
424 container->width = container->height = 0;
425 arrange_windows(sibling, -1, -1);
426 arrange_windows(old_parent, -1, -1);
427 }
428 break;
429 default:
430 sway_assert(0, "Not expecting to see container of type %s here",
431 container_type_to_str(sibling->type));
432 return;
433 }
172} 434}
173 435
174enum sway_container_layout container_get_default_layout( 436enum sway_container_layout container_get_default_layout(
@@ -320,6 +582,7 @@ void arrange_windows(struct sway_container *container,
320 container->children->length); 582 container->children->length);
321 break; 583 break;
322 } 584 }
585 container_damage_whole(container);
323} 586}
324 587
325static void apply_horiz_layout(struct sway_container *container, 588static void apply_horiz_layout(struct sway_container *container,
@@ -512,28 +775,6 @@ static void get_layout_center_position(struct sway_container *container,
512 } 775 }
513} 776}
514 777
515static bool sway_dir_to_wlr(enum movement_direction dir,
516 enum wlr_direction *out) {
517 switch (dir) {
518 case MOVE_UP:
519 *out = WLR_DIRECTION_UP;
520 break;
521 case MOVE_DOWN:
522 *out = WLR_DIRECTION_DOWN;
523 break;
524 case MOVE_LEFT:
525 *out = WLR_DIRECTION_LEFT;
526 break;
527 case MOVE_RIGHT:
528 *out = WLR_DIRECTION_RIGHT;
529 break;
530 default:
531 return false;
532 }
533
534 return true;
535}
536
537static struct sway_container *sway_output_from_wlr(struct wlr_output *output) { 778static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
538 if (output == NULL) { 779 if (output == NULL) {
539 return NULL; 780 return NULL;
@@ -673,6 +914,9 @@ struct sway_container *container_replace_child(struct sway_container *child,
673 int i = index_child(child); 914 int i = index_child(child);
674 915
675 // TODO floating 916 // TODO floating
917 if (new_child->parent) {
918 container_remove_child(new_child);
919 }
676 parent->children->items[i] = new_child; 920 parent->children->items[i] = new_child;
677 new_child->parent = parent; 921 new_child->parent = parent;
678 child->parent = NULL; 922 child->parent = NULL;
@@ -718,7 +962,9 @@ struct sway_container *container_split(struct sway_container *child,
718 } 962 }
719 963
720 container_add_child(workspace, cont); 964 container_add_child(workspace, cont);
965 enum sway_container_layout old_layout = workspace->layout;
721 container_set_layout(workspace, layout); 966 container_set_layout(workspace, layout);
967 cont->layout = old_layout;
722 968
723 if (set_focus) { 969 if (set_focus) {
724 seat_set_focus(seat, cont); 970 seat_set_focus(seat, cont);