diff options
Diffstat (limited to 'sway/tree/layout.c')
-rw-r--r-- | sway/tree/layout.c | 285 |
1 files changed, 276 insertions, 9 deletions
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 41ff81b2..3d04a1a7 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "sway/layout.h" | 10 | #include "sway/layout.h" |
11 | #include "sway/output.h" | 11 | #include "sway/output.h" |
12 | #include "sway/view.h" | 12 | #include "sway/view.h" |
13 | #include "sway/input/seat.h" | ||
13 | #include "list.h" | 14 | #include "list.h" |
14 | #include "log.h" | 15 | #include "log.h" |
15 | 16 | ||
@@ -48,10 +49,12 @@ void init_layout(void) { | |||
48 | root_container.layout = L_NONE; | 49 | root_container.layout = L_NONE; |
49 | root_container.name = strdup("root"); | 50 | root_container.name = strdup("root"); |
50 | root_container.children = create_list(); | 51 | root_container.children = create_list(); |
52 | wl_signal_init(&root_container.events.destroy); | ||
51 | 53 | ||
52 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); | 54 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); |
53 | root_container.sway_root->output_layout = wlr_output_layout_create(); | 55 | root_container.sway_root->output_layout = wlr_output_layout_create(); |
54 | wl_list_init(&root_container.sway_root->unmanaged_views); | 56 | wl_list_init(&root_container.sway_root->unmanaged_views); |
57 | wl_signal_init(&root_container.sway_root->events.new_container); | ||
55 | 58 | ||
56 | root_container.sway_root->output_layout_change.notify = | 59 | root_container.sway_root->output_layout_change.notify = |
57 | output_layout_change_notify; | 60 | output_layout_change_notify; |
@@ -59,6 +62,32 @@ void init_layout(void) { | |||
59 | &root_container.sway_root->output_layout_change); | 62 | &root_container.sway_root->output_layout_change); |
60 | } | 63 | } |
61 | 64 | ||
65 | static int index_child(const swayc_t *child) { | ||
66 | // TODO handle floating | ||
67 | swayc_t *parent = child->parent; | ||
68 | int i, len; | ||
69 | len = parent->children->length; | ||
70 | for (i = 0; i < len; ++i) { | ||
71 | if (parent->children->items[i] == child) { | ||
72 | break; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | if (!sway_assert(i < len, "Stray container")) { | ||
77 | return -1; | ||
78 | } | ||
79 | return i; | ||
80 | } | ||
81 | |||
82 | swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) { | ||
83 | // TODO handle floating | ||
84 | swayc_t *parent = fixed->parent; | ||
85 | int i = index_child(fixed); | ||
86 | list_insert(parent->children, i + 1, active); | ||
87 | active->parent = parent; | ||
88 | return active->parent; | ||
89 | } | ||
90 | |||
62 | void add_child(swayc_t *parent, swayc_t *child) { | 91 | void add_child(swayc_t *parent, swayc_t *child) { |
63 | wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", | 92 | wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", |
64 | child, child->type, child->width, child->height, | 93 | child, child->type, child->width, child->height, |
@@ -66,9 +95,6 @@ void add_child(swayc_t *parent, swayc_t *child) { | |||
66 | list_add(parent->children, child); | 95 | list_add(parent->children, child); |
67 | child->parent = parent; | 96 | child->parent = parent; |
68 | // set focus for this container | 97 | // set focus for this container |
69 | if (!parent->focused) { | ||
70 | parent->focused = child; | ||
71 | } | ||
72 | /* TODO WLR | 98 | /* TODO WLR |
73 | if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { | 99 | if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) { |
74 | child = new_container(child, parent->workspace_layout); | 100 | child = new_container(child, parent->workspace_layout); |
@@ -147,8 +173,8 @@ void arrange_windows(swayc_t *container, double width, double height) { | |||
147 | height = floor(height); | 173 | height = floor(height); |
148 | 174 | ||
149 | wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, | 175 | wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, |
150 | container->name, container->width, container->height, container->x, | 176 | container->name, container->width, container->height, container->x, |
151 | container->y); | 177 | container->y); |
152 | 178 | ||
153 | double x = 0, y = 0; | 179 | double x = 0, y = 0; |
154 | switch (container->type) { | 180 | switch (container->type) { |
@@ -249,8 +275,8 @@ static void apply_horiz_layout(swayc_t *container, | |||
249 | for (int i = start; i < end; ++i) { | 275 | for (int i = start; i < end; ++i) { |
250 | swayc_t *child = container->children->items[i]; | 276 | swayc_t *child = container->children->items[i]; |
251 | wlr_log(L_DEBUG, | 277 | wlr_log(L_DEBUG, |
252 | "Calculating arrangement for %p:%d (will scale %f by %f)", | 278 | "Calculating arrangement for %p:%d (will scale %f by %f)", |
253 | child, child->type, width, scale); | 279 | child, child->type, width, scale); |
254 | view_set_position(child->sway_view, child_x, y); | 280 | view_set_position(child->sway_view, child_x, y); |
255 | 281 | ||
256 | if (i == end - 1) { | 282 | if (i == end - 1) { |
@@ -299,8 +325,8 @@ void apply_vert_layout(swayc_t *container, | |||
299 | for (i = start; i < end; ++i) { | 325 | for (i = start; i < end; ++i) { |
300 | swayc_t *child = container->children->items[i]; | 326 | swayc_t *child = container->children->items[i]; |
301 | wlr_log(L_DEBUG, | 327 | wlr_log(L_DEBUG, |
302 | "Calculating arrangement for %p:%d (will scale %f by %f)", | 328 | "Calculating arrangement for %p:%d (will scale %f by %f)", |
303 | child, child->type, height, scale); | 329 | child, child->type, height, scale); |
304 | view_set_position(child->sway_view, x, child_y); | 330 | view_set_position(child->sway_view, x, child_y); |
305 | 331 | ||
306 | if (i == end - 1) { | 332 | if (i == end - 1) { |
@@ -321,3 +347,244 @@ void apply_vert_layout(swayc_t *container, | |||
321 | */ | 347 | */ |
322 | } | 348 | } |
323 | } | 349 | } |
350 | |||
351 | /** | ||
352 | * Get swayc in the direction of newly entered output. | ||
353 | */ | ||
354 | static swayc_t *get_swayc_in_output_direction(swayc_t *output, | ||
355 | enum movement_direction dir, struct sway_seat *seat) { | ||
356 | if (!output) { | ||
357 | return NULL; | ||
358 | } | ||
359 | |||
360 | swayc_t *ws = sway_seat_get_focus_inactive(seat, output); | ||
361 | if (ws->type != C_WORKSPACE) { | ||
362 | ws = swayc_parent_by_type(ws, C_WORKSPACE); | ||
363 | } | ||
364 | |||
365 | if (ws == NULL) { | ||
366 | wlr_log(L_ERROR, "got an output without a workspace"); | ||
367 | return NULL; | ||
368 | } | ||
369 | |||
370 | if (ws->children->length > 0) { | ||
371 | switch (dir) { | ||
372 | case MOVE_LEFT: | ||
373 | // get most right child of new output | ||
374 | return ws->children->items[ws->children->length-1]; | ||
375 | case MOVE_RIGHT: | ||
376 | // get most left child of new output | ||
377 | return ws->children->items[0]; | ||
378 | case MOVE_UP: | ||
379 | case MOVE_DOWN: { | ||
380 | swayc_t *focused = sway_seat_get_focus_inactive(seat, ws); | ||
381 | if (focused && focused->parent) { | ||
382 | swayc_t *parent = focused->parent; | ||
383 | if (parent->layout == L_VERT) { | ||
384 | if (dir == MOVE_UP) { | ||
385 | // get child furthest down on new output | ||
386 | return parent->children->items[parent->children->length-1]; | ||
387 | } else if (dir == MOVE_DOWN) { | ||
388 | // get child furthest up on new output | ||
389 | return parent->children->items[0]; | ||
390 | } | ||
391 | } | ||
392 | return focused; | ||
393 | } | ||
394 | break; | ||
395 | } | ||
396 | default: | ||
397 | break; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | return ws; | ||
402 | } | ||
403 | |||
404 | static void get_layout_center_position(swayc_t *container, int *x, int *y) { | ||
405 | // FIXME view coords are inconsistently referred to in layout/output systems | ||
406 | if (container->type == C_OUTPUT) { | ||
407 | *x = container->x + container->width/2; | ||
408 | *y = container->y + container->height/2; | ||
409 | } else { | ||
410 | swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); | ||
411 | if (container->type == C_WORKSPACE) { | ||
412 | // Workspace coordinates are actually wrong/arbitrary, but should | ||
413 | // be same as output. | ||
414 | *x = output->x; | ||
415 | *y = output->y; | ||
416 | } else { | ||
417 | *x = output->x + container->x; | ||
418 | *y = output->y + container->y; | ||
419 | } | ||
420 | } | ||
421 | } | ||
422 | |||
423 | static bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) { | ||
424 | switch (dir) { | ||
425 | case MOVE_UP: | ||
426 | *out = WLR_DIRECTION_UP; | ||
427 | break; | ||
428 | case MOVE_DOWN: | ||
429 | *out = WLR_DIRECTION_DOWN; | ||
430 | break; | ||
431 | case MOVE_LEFT: | ||
432 | *out = WLR_DIRECTION_LEFT; | ||
433 | break; | ||
434 | case MOVE_RIGHT: | ||
435 | *out = WLR_DIRECTION_RIGHT; | ||
436 | break; | ||
437 | default: | ||
438 | return false; | ||
439 | } | ||
440 | |||
441 | return true; | ||
442 | } | ||
443 | |||
444 | static swayc_t *sway_output_from_wlr(struct wlr_output *output) { | ||
445 | if (output == NULL) { | ||
446 | return NULL; | ||
447 | } | ||
448 | for (int i = 0; i < root_container.children->length; ++i) { | ||
449 | swayc_t *o = root_container.children->items[i]; | ||
450 | if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { | ||
451 | return o; | ||
452 | } | ||
453 | } | ||
454 | return NULL; | ||
455 | } | ||
456 | |||
457 | static swayc_t *get_swayc_in_direction_under(swayc_t *container, | ||
458 | enum movement_direction dir, struct sway_seat *seat, swayc_t *limit) { | ||
459 | if (dir == MOVE_CHILD) { | ||
460 | return sway_seat_get_focus_inactive(seat, container); | ||
461 | } | ||
462 | |||
463 | swayc_t *parent = container->parent; | ||
464 | if (dir == MOVE_PARENT) { | ||
465 | if (parent->type == C_OUTPUT) { | ||
466 | return NULL; | ||
467 | } else { | ||
468 | return parent; | ||
469 | } | ||
470 | } | ||
471 | |||
472 | if (dir == MOVE_PREV || dir == MOVE_NEXT) { | ||
473 | int focused_idx = index_child(container); | ||
474 | if (focused_idx == -1) { | ||
475 | return NULL; | ||
476 | } else { | ||
477 | int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) % | ||
478 | parent->children->length; | ||
479 | if (desired < 0) { | ||
480 | desired += parent->children->length; | ||
481 | } | ||
482 | return parent->children->items[desired]; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | // If moving to an adjacent output we need a starting position (since this | ||
487 | // output might border to multiple outputs). | ||
488 | //struct wlc_point abs_pos; | ||
489 | //get_layout_center_position(container, &abs_pos); | ||
490 | |||
491 | |||
492 | // TODO WLR fullscreen | ||
493 | /* | ||
494 | if (container->type == C_VIEW && swayc_is_fullscreen(container)) { | ||
495 | wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output"); | ||
496 | container = swayc_parent_by_type(container, C_OUTPUT); | ||
497 | get_layout_center_position(container, &abs_pos); | ||
498 | swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true); | ||
499 | return get_swayc_in_output_direction(output, dir); | ||
500 | } | ||
501 | if (container->type == C_WORKSPACE && container->fullscreen) { | ||
502 | sway_log(L_DEBUG, "Moving to fullscreen view"); | ||
503 | return container->fullscreen; | ||
504 | } | ||
505 | */ | ||
506 | |||
507 | swayc_t *wrap_candidate = NULL; | ||
508 | while (true) { | ||
509 | // Test if we can even make a difference here | ||
510 | bool can_move = false; | ||
511 | int desired; | ||
512 | int idx = index_child(container); | ||
513 | if (parent->type == C_ROOT) { | ||
514 | enum wlr_direction wlr_dir = 0; | ||
515 | if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), | ||
516 | "got invalid direction: %d", dir)) { | ||
517 | return NULL; | ||
518 | } | ||
519 | int lx, ly; | ||
520 | get_layout_center_position(container, &lx, &ly); | ||
521 | struct wlr_output_layout *layout = root_container.sway_root->output_layout; | ||
522 | struct wlr_output *wlr_adjacent = | ||
523 | wlr_output_layout_adjacent_output(layout, wlr_dir, | ||
524 | container->sway_output->wlr_output, lx, ly); | ||
525 | swayc_t *adjacent = sway_output_from_wlr(wlr_adjacent); | ||
526 | |||
527 | if (!adjacent || adjacent == container) { | ||
528 | return wrap_candidate; | ||
529 | } | ||
530 | swayc_t *next = get_swayc_in_output_direction(adjacent, dir, seat); | ||
531 | if (next == NULL) { | ||
532 | return NULL; | ||
533 | } | ||
534 | if (next->children && next->children->length) { | ||
535 | // TODO consider floating children as well | ||
536 | return sway_seat_get_focus_inactive(seat, next); | ||
537 | } else { | ||
538 | return next; | ||
539 | } | ||
540 | } else { | ||
541 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | ||
542 | if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { | ||
543 | can_move = true; | ||
544 | desired = idx + (dir == MOVE_LEFT ? -1 : 1); | ||
545 | } | ||
546 | } else { | ||
547 | if (parent->layout == L_VERT || parent->layout == L_STACKED) { | ||
548 | can_move = true; | ||
549 | desired = idx + (dir == MOVE_UP ? -1 : 1); | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | |||
554 | if (can_move) { | ||
555 | // TODO handle floating | ||
556 | if (desired < 0 || desired >= parent->children->length) { | ||
557 | can_move = false; | ||
558 | int len = parent->children->length; | ||
559 | if (!wrap_candidate && len > 1) { | ||
560 | if (desired < 0) { | ||
561 | wrap_candidate = parent->children->items[len-1]; | ||
562 | } else { | ||
563 | wrap_candidate = parent->children->items[0]; | ||
564 | } | ||
565 | if (config->force_focus_wrapping) { | ||
566 | return wrap_candidate; | ||
567 | } | ||
568 | } | ||
569 | } else { | ||
570 | wlr_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__, | ||
571 | idx, container, dir, desired, parent->children->items[desired]); | ||
572 | return parent->children->items[desired]; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | if (!can_move) { | ||
577 | container = parent; | ||
578 | parent = parent->parent; | ||
579 | if (!parent || container == limit) { | ||
580 | // wrapping is the last chance | ||
581 | return wrap_candidate; | ||
582 | } | ||
583 | } | ||
584 | } | ||
585 | } | ||
586 | |||
587 | swayc_t *get_swayc_in_direction(swayc_t *container, struct sway_seat *seat, | ||
588 | enum movement_direction dir) { | ||
589 | return get_swayc_in_direction_under(container, dir, seat, NULL); | ||
590 | } | ||