diff options
Diffstat (limited to 'sway/tree')
-rw-r--r-- | sway/tree/arrange.c | 18 | ||||
-rw-r--r-- | sway/tree/container.c | 369 | ||||
-rw-r--r-- | sway/tree/layout.c | 122 | ||||
-rw-r--r-- | sway/tree/output.c | 2 | ||||
-rw-r--r-- | sway/tree/view.c | 315 | ||||
-rw-r--r-- | sway/tree/workspace.c | 268 |
6 files changed, 762 insertions, 332 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 533cf71c..5452b13c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -220,8 +220,22 @@ static void arrange_workspace(struct sway_container *workspace) { | |||
220 | container_set_dirty(workspace); | 220 | container_set_dirty(workspace); |
221 | wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, | 221 | wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, |
222 | workspace->x, workspace->y); | 222 | workspace->x, workspace->y); |
223 | arrange_floating(workspace->sway_workspace->floating); | 223 | if (workspace->sway_workspace->fullscreen) { |
224 | arrange_children_of(workspace); | 224 | struct sway_container *fs = workspace->sway_workspace->fullscreen; |
225 | fs->x = workspace->parent->x; | ||
226 | fs->y = workspace->parent->y; | ||
227 | fs->width = workspace->parent->width; | ||
228 | fs->height = workspace->parent->height; | ||
229 | if (fs->type == C_VIEW) { | ||
230 | view_autoconfigure(fs->sway_view); | ||
231 | } else { | ||
232 | arrange_children_of(fs); | ||
233 | } | ||
234 | container_set_dirty(fs); | ||
235 | } else { | ||
236 | arrange_floating(workspace->sway_workspace->floating); | ||
237 | arrange_children_of(workspace); | ||
238 | } | ||
225 | } | 239 | } |
226 | 240 | ||
227 | static void arrange_output(struct sway_container *output) { | 241 | static void arrange_output(struct sway_container *output) { |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 4dbfbb29..46c54e2d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "sway/input/seat.h" | 17 | #include "sway/input/seat.h" |
18 | #include "sway/ipc-server.h" | 18 | #include "sway/ipc-server.h" |
19 | #include "sway/output.h" | 19 | #include "sway/output.h" |
20 | #include "sway/scratchpad.h" | ||
20 | #include "sway/server.h" | 21 | #include "sway/server.h" |
21 | #include "sway/tree/arrange.h" | 22 | #include "sway/tree/arrange.h" |
22 | #include "sway/tree/layout.h" | 23 | #include "sway/tree/layout.h" |
@@ -61,13 +62,17 @@ void container_create_notify(struct sway_container *container) { | |||
61 | // TODO send ipc event type based on the container type | 62 | // TODO send ipc event type based on the container type |
62 | wl_signal_emit(&root_container.sway_root->events.new_container, container); | 63 | wl_signal_emit(&root_container.sway_root->events.new_container, container); |
63 | 64 | ||
64 | if (container->type == C_VIEW || container->type == C_CONTAINER) { | 65 | if (container->type == C_VIEW) { |
65 | ipc_event_window(container, "new"); | 66 | ipc_event_window(container, "new"); |
67 | } else if (container->type == C_WORKSPACE) { | ||
68 | ipc_event_workspace(NULL, container, "init"); | ||
66 | } | 69 | } |
67 | } | 70 | } |
68 | 71 | ||
69 | static void container_update_textures_recursive(struct sway_container *con) { | 72 | void container_update_textures_recursive(struct sway_container *con) { |
70 | container_update_title_textures(con); | 73 | if (con->type == C_CONTAINER || con->type == C_VIEW) { |
74 | container_update_title_textures(con); | ||
75 | } | ||
71 | 76 | ||
72 | if (con->type == C_VIEW) { | 77 | if (con->type == C_VIEW) { |
73 | view_update_marks_textures(con->sway_view); | 78 | view_update_marks_textures(con->sway_view); |
@@ -76,6 +81,10 @@ static void container_update_textures_recursive(struct sway_container *con) { | |||
76 | struct sway_container *child = con->children->items[i]; | 81 | struct sway_container *child = con->children->items[i]; |
77 | container_update_textures_recursive(child); | 82 | container_update_textures_recursive(child); |
78 | } | 83 | } |
84 | |||
85 | if (con->type == C_WORKSPACE) { | ||
86 | container_update_textures_recursive(con->sway_workspace->floating); | ||
87 | } | ||
79 | } | 88 | } |
80 | } | 89 | } |
81 | 90 | ||
@@ -139,8 +148,6 @@ struct sway_container *container_create(enum sway_container_type type) { | |||
139 | static void container_workspace_free(struct sway_workspace *ws) { | 148 | static void container_workspace_free(struct sway_workspace *ws) { |
140 | list_foreach(ws->output_priority, free); | 149 | list_foreach(ws->output_priority, free); |
141 | list_free(ws->output_priority); | 150 | list_free(ws->output_priority); |
142 | ws->floating->destroying = true; | ||
143 | container_free(ws->floating); | ||
144 | free(ws); | 151 | free(ws); |
145 | } | 152 | } |
146 | 153 | ||
@@ -193,6 +200,9 @@ void container_free(struct sway_container *cont) { | |||
193 | free(cont); | 200 | free(cont); |
194 | } | 201 | } |
195 | 202 | ||
203 | static struct sway_container *container_destroy_noreaping( | ||
204 | struct sway_container *con); | ||
205 | |||
196 | static struct sway_container *container_workspace_destroy( | 206 | static struct sway_container *container_workspace_destroy( |
197 | struct sway_container *workspace) { | 207 | struct sway_container *workspace) { |
198 | if (!sway_assert(workspace, "cannot destroy null workspace")) { | 208 | if (!sway_assert(workspace, "cannot destroy null workspace")) { |
@@ -237,6 +247,8 @@ static struct sway_container *container_workspace_destroy( | |||
237 | } | 247 | } |
238 | } | 248 | } |
239 | 249 | ||
250 | container_destroy_noreaping(workspace->sway_workspace->floating); | ||
251 | |||
240 | return output; | 252 | return output; |
241 | } | 253 | } |
242 | 254 | ||
@@ -271,7 +283,7 @@ static struct sway_container *container_output_destroy( | |||
271 | container_remove_child(workspace); | 283 | container_remove_child(workspace); |
272 | if (!workspace_is_empty(workspace)) { | 284 | if (!workspace_is_empty(workspace)) { |
273 | container_add_child(new_output, workspace); | 285 | container_add_child(new_output, workspace); |
274 | ipc_event_workspace(workspace, NULL, "move"); | 286 | ipc_event_workspace(NULL, workspace, "move"); |
275 | } else { | 287 | } else { |
276 | container_destroy(workspace); | 288 | container_destroy(workspace); |
277 | } | 289 | } |
@@ -309,7 +321,13 @@ static struct sway_container *container_destroy_noreaping( | |||
309 | } | 321 | } |
310 | 322 | ||
311 | wl_signal_emit(&con->events.destroy, con); | 323 | wl_signal_emit(&con->events.destroy, con); |
312 | ipc_event_window(con, "close"); | 324 | |
325 | // emit IPC event | ||
326 | if (con->type == C_VIEW) { | ||
327 | ipc_event_window(con, "close"); | ||
328 | } else if (con->type == C_WORKSPACE) { | ||
329 | ipc_event_workspace(NULL, con, "empty"); | ||
330 | } | ||
313 | 331 | ||
314 | // The below functions move their children to somewhere else. | 332 | // The below functions move their children to somewhere else. |
315 | if (con->type == C_OUTPUT) { | 333 | if (con->type == C_OUTPUT) { |
@@ -323,9 +341,15 @@ static struct sway_container *container_destroy_noreaping( | |||
323 | } | 341 | } |
324 | } | 342 | } |
325 | 343 | ||
344 | container_end_mouse_operation(con); | ||
345 | |||
326 | con->destroying = true; | 346 | con->destroying = true; |
327 | container_set_dirty(con); | 347 | container_set_dirty(con); |
328 | 348 | ||
349 | if (con->scratchpad) { | ||
350 | scratchpad_remove_container(con); | ||
351 | } | ||
352 | |||
329 | if (!con->parent) { | 353 | if (!con->parent) { |
330 | return NULL; | 354 | return NULL; |
331 | } | 355 | } |
@@ -398,6 +422,10 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
398 | * This function just wraps container_destroy_noreaping(), then does reaping. | 422 | * This function just wraps container_destroy_noreaping(), then does reaping. |
399 | */ | 423 | */ |
400 | struct sway_container *container_destroy(struct sway_container *con) { | 424 | struct sway_container *container_destroy(struct sway_container *con) { |
425 | if (con->is_fullscreen) { | ||
426 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
427 | ws->sway_workspace->fullscreen = NULL; | ||
428 | } | ||
401 | struct sway_container *parent = container_destroy_noreaping(con); | 429 | struct sway_container *parent = container_destroy_noreaping(con); |
402 | 430 | ||
403 | if (!parent) { | 431 | if (!parent) { |
@@ -507,7 +535,7 @@ struct sway_container *container_parent(struct sway_container *container, | |||
507 | return container; | 535 | return container; |
508 | } | 536 | } |
509 | 537 | ||
510 | static struct sway_container *container_at_view(struct sway_container *swayc, | 538 | struct sway_container *container_at_view(struct sway_container *swayc, |
511 | double lx, double ly, | 539 | double lx, double ly, |
512 | struct wlr_surface **surface, double *sx, double *sy) { | 540 | struct wlr_surface **surface, double *sx, double *sy) { |
513 | if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { | 541 | if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { |
@@ -520,10 +548,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc, | |||
520 | double _sx, _sy; | 548 | double _sx, _sy; |
521 | struct wlr_surface *_surface = NULL; | 549 | struct wlr_surface *_surface = NULL; |
522 | switch (sview->type) { | 550 | switch (sview->type) { |
551 | #ifdef HAVE_XWAYLAND | ||
523 | case SWAY_VIEW_XWAYLAND: | 552 | case SWAY_VIEW_XWAYLAND: |
524 | _surface = wlr_surface_surface_at(sview->surface, | 553 | _surface = wlr_surface_surface_at(sview->surface, |
525 | view_sx, view_sy, &_sx, &_sy); | 554 | view_sx, view_sy, &_sx, &_sy); |
526 | break; | 555 | break; |
556 | #endif | ||
527 | case SWAY_VIEW_XDG_SHELL_V6: | 557 | case SWAY_VIEW_XDG_SHELL_V6: |
528 | _surface = wlr_xdg_surface_v6_surface_at( | 558 | _surface = wlr_xdg_surface_v6_surface_at( |
529 | sview->wlr_xdg_surface_v6, | 559 | sview->wlr_xdg_surface_v6, |
@@ -539,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc, | |||
539 | *sx = _sx; | 569 | *sx = _sx; |
540 | *sy = _sy; | 570 | *sy = _sy; |
541 | *surface = _surface; | 571 | *surface = _surface; |
572 | return swayc; | ||
542 | } | 573 | } |
543 | return swayc; | 574 | return NULL; |
544 | } | 575 | } |
545 | 576 | ||
577 | static struct sway_container *tiling_container_at( | ||
578 | struct sway_container *con, double lx, double ly, | ||
579 | struct wlr_surface **surface, double *sx, double *sy); | ||
580 | |||
546 | /** | 581 | /** |
547 | * container_at for a container with layout L_TABBED. | 582 | * container_at for a container with layout L_TABBED. |
548 | */ | 583 | */ |
@@ -569,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, | |||
569 | // Surfaces | 604 | // Surfaces |
570 | struct sway_container *current = seat_get_active_child(seat, parent); | 605 | struct sway_container *current = seat_get_active_child(seat, parent); |
571 | 606 | ||
572 | return container_at(current, lx, ly, surface, sx, sy); | 607 | return tiling_container_at(current, lx, ly, surface, sx, sy); |
573 | } | 608 | } |
574 | 609 | ||
575 | /** | 610 | /** |
@@ -594,7 +629,7 @@ static struct sway_container *container_at_stacked( | |||
594 | // Surfaces | 629 | // Surfaces |
595 | struct sway_container *current = seat_get_active_child(seat, parent); | 630 | struct sway_container *current = seat_get_active_child(seat, parent); |
596 | 631 | ||
597 | return container_at(current, lx, ly, surface, sx, sy); | 632 | return tiling_container_at(current, lx, ly, surface, sx, sy); |
598 | } | 633 | } |
599 | 634 | ||
600 | /** | 635 | /** |
@@ -612,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent, | |||
612 | .height = child->height, | 647 | .height = child->height, |
613 | }; | 648 | }; |
614 | if (wlr_box_contains_point(&box, lx, ly)) { | 649 | if (wlr_box_contains_point(&box, lx, ly)) { |
615 | return container_at(child, lx, ly, surface, sx, sy); | 650 | return tiling_container_at(child, lx, ly, surface, sx, sy); |
616 | } | 651 | } |
617 | } | 652 | } |
618 | return NULL; | 653 | return NULL; |
619 | } | 654 | } |
620 | 655 | ||
621 | struct sway_container *container_at(struct sway_container *parent, | 656 | static struct sway_container *floating_container_at(double lx, double ly, |
622 | double lx, double ly, | ||
623 | struct wlr_surface **surface, double *sx, double *sy) { | ||
624 | if (!sway_assert(parent->type >= C_WORKSPACE, | ||
625 | "Expected workspace or deeper")) { | ||
626 | return NULL; | ||
627 | } | ||
628 | if (parent->type == C_VIEW) { | ||
629 | return container_at_view(parent, lx, ly, surface, sx, sy); | ||
630 | } | ||
631 | if (!parent->children->length) { | ||
632 | return NULL; | ||
633 | } | ||
634 | |||
635 | switch (parent->layout) { | ||
636 | case L_HORIZ: | ||
637 | case L_VERT: | ||
638 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
639 | case L_TABBED: | ||
640 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
641 | case L_STACKED: | ||
642 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
643 | case L_FLOATING: | ||
644 | sway_assert(false, "Didn't expect to see floating here"); | ||
645 | return NULL; | ||
646 | case L_NONE: | ||
647 | return NULL; | ||
648 | } | ||
649 | |||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | struct sway_container *floating_container_at(double lx, double ly, | ||
654 | struct wlr_surface **surface, double *sx, double *sy) { | 657 | struct wlr_surface **surface, double *sx, double *sy) { |
655 | for (int i = 0; i < root_container.children->length; ++i) { | 658 | for (int i = 0; i < root_container.children->length; ++i) { |
656 | struct sway_container *output = root_container.children->items[i]; | 659 | struct sway_container *output = root_container.children->items[i]; |
@@ -672,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
672 | .height = floater->height, | 675 | .height = floater->height, |
673 | }; | 676 | }; |
674 | if (wlr_box_contains_point(&box, lx, ly)) { | 677 | if (wlr_box_contains_point(&box, lx, ly)) { |
675 | return container_at(floater, lx, ly, surface, sx, sy); | 678 | return tiling_container_at(floater, lx, ly, |
679 | surface, sx, sy); | ||
676 | } | 680 | } |
677 | } | 681 | } |
678 | } | 682 | } |
@@ -680,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
680 | return NULL; | 684 | return NULL; |
681 | } | 685 | } |
682 | 686 | ||
687 | static struct sway_container *tiling_container_at( | ||
688 | struct sway_container *con, double lx, double ly, | ||
689 | struct wlr_surface **surface, double *sx, double *sy) { | ||
690 | if (con->type == C_VIEW) { | ||
691 | return container_at_view(con, lx, ly, surface, sx, sy); | ||
692 | } | ||
693 | if (!con->children->length) { | ||
694 | return NULL; | ||
695 | } | ||
696 | |||
697 | switch (con->layout) { | ||
698 | case L_HORIZ: | ||
699 | case L_VERT: | ||
700 | return container_at_linear(con, lx, ly, surface, sx, sy); | ||
701 | case L_TABBED: | ||
702 | return container_at_tabbed(con, lx, ly, surface, sx, sy); | ||
703 | case L_STACKED: | ||
704 | return container_at_stacked(con, lx, ly, surface, sx, sy); | ||
705 | case L_FLOATING: | ||
706 | sway_assert(false, "Didn't expect to see floating here"); | ||
707 | return NULL; | ||
708 | case L_NONE: | ||
709 | return NULL; | ||
710 | } | ||
711 | return NULL; | ||
712 | } | ||
713 | |||
714 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
715 | if (wlr_surface_is_xdg_surface(surface)) { | ||
716 | struct wlr_xdg_surface *xdg_surface = | ||
717 | wlr_xdg_surface_from_wlr_surface(surface); | ||
718 | while (xdg_surface) { | ||
719 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
720 | return true; | ||
721 | } | ||
722 | xdg_surface = xdg_surface->toplevel->parent; | ||
723 | } | ||
724 | return false; | ||
725 | } | ||
726 | |||
727 | if (wlr_surface_is_xdg_surface_v6(surface)) { | ||
728 | struct wlr_xdg_surface_v6 *xdg_surface_v6 = | ||
729 | wlr_xdg_surface_v6_from_wlr_surface(surface); | ||
730 | while (xdg_surface_v6) { | ||
731 | if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { | ||
732 | return true; | ||
733 | } | ||
734 | xdg_surface_v6 = xdg_surface_v6->toplevel->parent; | ||
735 | } | ||
736 | return false; | ||
737 | } | ||
738 | |||
739 | return false; | ||
740 | } | ||
741 | |||
742 | struct sway_container *container_at(struct sway_container *workspace, | ||
743 | double lx, double ly, | ||
744 | struct wlr_surface **surface, double *sx, double *sy) { | ||
745 | if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { | ||
746 | return NULL; | ||
747 | } | ||
748 | struct sway_container *c; | ||
749 | // Focused view's popups | ||
750 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
751 | struct sway_container *focus = | ||
752 | seat_get_focus_inactive(seat, &root_container); | ||
753 | if (focus && focus->type == C_VIEW) { | ||
754 | container_at_view(focus, lx, ly, surface, sx, sy); | ||
755 | if (*surface && surface_is_popup(*surface)) { | ||
756 | return focus; | ||
757 | } | ||
758 | *surface = NULL; | ||
759 | } | ||
760 | // Floating | ||
761 | if ((c = floating_container_at(lx, ly, surface, sx, sy))) { | ||
762 | return c; | ||
763 | } | ||
764 | // Tiling | ||
765 | if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { | ||
766 | return c; | ||
767 | } | ||
768 | return NULL; | ||
769 | } | ||
770 | |||
683 | void container_for_each_descendant_dfs(struct sway_container *container, | 771 | void container_for_each_descendant_dfs(struct sway_container *container, |
684 | void (*f)(struct sway_container *container, void *data), | 772 | void (*f)(struct sway_container *container, void *data), |
685 | void *data) { | 773 | void *data) { |
@@ -934,36 +1022,104 @@ size_t container_titlebar_height() { | |||
934 | return config->font_height + TITLEBAR_V_PADDING * 2; | 1022 | return config->font_height + TITLEBAR_V_PADDING * 2; |
935 | } | 1023 | } |
936 | 1024 | ||
1025 | void container_init_floating(struct sway_container *con) { | ||
1026 | if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, | ||
1027 | "Expected a view or container")) { | ||
1028 | return; | ||
1029 | } | ||
1030 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
1031 | int min_width, min_height; | ||
1032 | int max_width, max_height; | ||
1033 | |||
1034 | if (config->floating_minimum_width == -1) { // no minimum | ||
1035 | min_width = 0; | ||
1036 | } else if (config->floating_minimum_width == 0) { // automatic | ||
1037 | min_width = 75; | ||
1038 | } else { | ||
1039 | min_width = config->floating_minimum_width; | ||
1040 | } | ||
1041 | |||
1042 | if (config->floating_minimum_height == -1) { // no minimum | ||
1043 | min_height = 0; | ||
1044 | } else if (config->floating_minimum_height == 0) { // automatic | ||
1045 | min_height = 50; | ||
1046 | } else { | ||
1047 | min_height = config->floating_minimum_height; | ||
1048 | } | ||
1049 | |||
1050 | if (config->floating_maximum_width == -1) { // no maximum | ||
1051 | max_width = INT_MAX; | ||
1052 | } else if (config->floating_maximum_width == 0) { // automatic | ||
1053 | max_width = ws->width * 0.6666; | ||
1054 | } else { | ||
1055 | max_width = config->floating_maximum_width; | ||
1056 | } | ||
1057 | |||
1058 | if (config->floating_maximum_height == -1) { // no maximum | ||
1059 | max_height = INT_MAX; | ||
1060 | } else if (config->floating_maximum_height == 0) { // automatic | ||
1061 | max_height = ws->height * 0.6666; | ||
1062 | } else { | ||
1063 | max_height = config->floating_maximum_height; | ||
1064 | } | ||
1065 | |||
1066 | if (con->type == C_CONTAINER) { | ||
1067 | con->width = max_width; | ||
1068 | con->height = max_height; | ||
1069 | con->x = ws->x + (ws->width - con->width) / 2; | ||
1070 | con->y = ws->y + (ws->height - con->height) / 2; | ||
1071 | } else { | ||
1072 | struct sway_view *view = con->sway_view; | ||
1073 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
1074 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
1075 | view->x = ws->x + (ws->width - view->width) / 2; | ||
1076 | view->y = ws->y + (ws->height - view->height) / 2; | ||
1077 | |||
1078 | // If the view's border is B_NONE then these properties are ignored. | ||
1079 | view->border_top = view->border_bottom = true; | ||
1080 | view->border_left = view->border_right = true; | ||
1081 | |||
1082 | container_set_geometry_from_floating_view(view->swayc); | ||
1083 | } | ||
1084 | } | ||
1085 | |||
937 | void container_set_floating(struct sway_container *container, bool enable) { | 1086 | void container_set_floating(struct sway_container *container, bool enable) { |
938 | if (container_is_floating(container) == enable) { | 1087 | if (container_is_floating(container) == enable) { |
939 | return; | 1088 | return; |
940 | } | 1089 | } |
941 | 1090 | ||
942 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | ||
943 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 1091 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
1092 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | ||
944 | 1093 | ||
945 | if (enable) { | 1094 | if (enable) { |
946 | container_remove_child(container); | 1095 | container_remove_child(container); |
947 | container_add_child(workspace->sway_workspace->floating, container); | 1096 | container_add_child(workspace->sway_workspace->floating, container); |
1097 | container_init_floating(container); | ||
948 | if (container->type == C_VIEW) { | 1098 | if (container->type == C_VIEW) { |
949 | view_init_floating(container->sway_view); | ||
950 | view_set_tiled(container->sway_view, false); | 1099 | view_set_tiled(container->sway_view, false); |
951 | } | 1100 | } |
952 | seat_set_focus(seat, seat_get_focus_inactive(seat, container)); | ||
953 | container_reap_empty_recursive(workspace); | ||
954 | } else { | 1101 | } else { |
955 | // Returning to tiled | 1102 | // Returning to tiled |
1103 | if (container->scratchpad) { | ||
1104 | scratchpad_remove_container(container); | ||
1105 | } | ||
956 | container_remove_child(container); | 1106 | container_remove_child(container); |
957 | container_add_child(workspace, container); | 1107 | struct sway_container *reference = |
1108 | seat_get_focus_inactive_tiling(seat, workspace); | ||
1109 | if (reference->type == C_VIEW) { | ||
1110 | reference = reference->parent; | ||
1111 | } | ||
1112 | container_add_child(reference, container); | ||
958 | container->width = container->parent->width; | 1113 | container->width = container->parent->width; |
959 | container->height = container->parent->height; | 1114 | container->height = container->parent->height; |
960 | if (container->type == C_VIEW) { | 1115 | if (container->type == C_VIEW) { |
961 | view_set_tiled(container->sway_view, true); | 1116 | view_set_tiled(container->sway_view, true); |
962 | } | 1117 | } |
963 | container->is_sticky = false; | 1118 | container->is_sticky = false; |
964 | container_reap_empty_recursive(workspace->sway_workspace->floating); | ||
965 | } | 1119 | } |
966 | 1120 | ||
1121 | container_end_mouse_operation(container); | ||
1122 | |||
967 | ipc_event_window(container, "floating"); | 1123 | ipc_event_window(container, "floating"); |
968 | } | 1124 | } |
969 | 1125 | ||
@@ -1009,7 +1165,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
1009 | /** | 1165 | /** |
1010 | * Translate the container's position as well as all children. | 1166 | * Translate the container's position as well as all children. |
1011 | */ | 1167 | */ |
1012 | static void container_floating_translate(struct sway_container *con, | 1168 | void container_floating_translate(struct sway_container *con, |
1013 | double x_amount, double y_amount) { | 1169 | double x_amount, double y_amount) { |
1014 | con->x += x_amount; | 1170 | con->x += x_amount; |
1015 | con->y += y_amount; | 1171 | con->y += y_amount; |
@@ -1105,3 +1261,110 @@ static bool find_urgent_iterator(struct sway_container *con, | |||
1105 | bool container_has_urgent_child(struct sway_container *container) { | 1261 | bool container_has_urgent_child(struct sway_container *container) { |
1106 | return container_find(container, find_urgent_iterator, NULL); | 1262 | return container_find(container, find_urgent_iterator, NULL); |
1107 | } | 1263 | } |
1264 | |||
1265 | void container_end_mouse_operation(struct sway_container *container) { | ||
1266 | struct sway_seat *seat; | ||
1267 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
1268 | if (seat->op_container == container) { | ||
1269 | seat_end_mouse_operation(seat); | ||
1270 | } | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1274 | static void set_fullscreen_iterator(struct sway_container *con, void *data) { | ||
1275 | if (con->type != C_VIEW) { | ||
1276 | return; | ||
1277 | } | ||
1278 | if (con->sway_view->impl->set_fullscreen) { | ||
1279 | bool *enable = data; | ||
1280 | con->sway_view->impl->set_fullscreen(con->sway_view, *enable); | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1284 | void container_set_fullscreen(struct sway_container *container, bool enable) { | ||
1285 | if (container->is_fullscreen == enable) { | ||
1286 | return; | ||
1287 | } | ||
1288 | |||
1289 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | ||
1290 | if (enable && workspace->sway_workspace->fullscreen) { | ||
1291 | container_set_fullscreen(workspace->sway_workspace->fullscreen, false); | ||
1292 | } | ||
1293 | |||
1294 | container_for_each_descendant_dfs(container, | ||
1295 | set_fullscreen_iterator, &enable); | ||
1296 | |||
1297 | container->is_fullscreen = enable; | ||
1298 | |||
1299 | if (enable) { | ||
1300 | workspace->sway_workspace->fullscreen = container; | ||
1301 | container->saved_x = container->x; | ||
1302 | container->saved_y = container->y; | ||
1303 | container->saved_width = container->width; | ||
1304 | container->saved_height = container->height; | ||
1305 | |||
1306 | struct sway_seat *seat; | ||
1307 | struct sway_container *focus, *focus_ws; | ||
1308 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
1309 | focus = seat_get_focus(seat); | ||
1310 | if (focus) { | ||
1311 | focus_ws = focus; | ||
1312 | if (focus_ws->type != C_WORKSPACE) { | ||
1313 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | ||
1314 | } | ||
1315 | if (focus_ws == workspace) { | ||
1316 | seat_set_focus(seat, container); | ||
1317 | } | ||
1318 | } | ||
1319 | } | ||
1320 | } else { | ||
1321 | workspace->sway_workspace->fullscreen = NULL; | ||
1322 | if (container_is_floating(container)) { | ||
1323 | container->x = container->saved_x; | ||
1324 | container->y = container->saved_y; | ||
1325 | container->width = container->saved_width; | ||
1326 | container->height = container->saved_height; | ||
1327 | } else { | ||
1328 | container->width = container->saved_width; | ||
1329 | container->height = container->saved_height; | ||
1330 | } | ||
1331 | } | ||
1332 | |||
1333 | container_end_mouse_operation(container); | ||
1334 | |||
1335 | ipc_event_window(container, "fullscreen_mode"); | ||
1336 | } | ||
1337 | |||
1338 | bool container_is_floating_or_child(struct sway_container *container) { | ||
1339 | do { | ||
1340 | if (container->parent && container->parent->layout == L_FLOATING) { | ||
1341 | return true; | ||
1342 | } | ||
1343 | container = container->parent; | ||
1344 | } while (container && container->type != C_WORKSPACE); | ||
1345 | |||
1346 | return false; | ||
1347 | } | ||
1348 | |||
1349 | bool container_is_fullscreen_or_child(struct sway_container *container) { | ||
1350 | do { | ||
1351 | if (container->is_fullscreen) { | ||
1352 | return true; | ||
1353 | } | ||
1354 | container = container->parent; | ||
1355 | } while (container && container->type != C_WORKSPACE); | ||
1356 | |||
1357 | return false; | ||
1358 | } | ||
1359 | |||
1360 | struct sway_container *container_wrap_children(struct sway_container *parent) { | ||
1361 | struct sway_container *middle = container_create(C_CONTAINER); | ||
1362 | middle->layout = parent->layout; | ||
1363 | while (parent->children->length) { | ||
1364 | struct sway_container *child = parent->children->items[0]; | ||
1365 | container_remove_child(child); | ||
1366 | container_add_child(middle, child); | ||
1367 | } | ||
1368 | container_add_child(parent, middle); | ||
1369 | return middle; | ||
1370 | } | ||
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1f898f8a..1f82e534 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <string.h> | 6 | #include <string.h> |
7 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
9 | #include "config.h" | ||
9 | #include "sway/debug.h" | 10 | #include "sway/debug.h" |
10 | #include "sway/tree/arrange.h" | 11 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/container.h" | 12 | #include "sway/tree/container.h" |
@@ -39,9 +40,12 @@ void layout_init(void) { | |||
39 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); | 40 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); |
40 | root_container.sway_root->output_layout = wlr_output_layout_create(); | 41 | root_container.sway_root->output_layout = wlr_output_layout_create(); |
41 | wl_list_init(&root_container.sway_root->outputs); | 42 | wl_list_init(&root_container.sway_root->outputs); |
43 | #ifdef HAVE_XWAYLAND | ||
42 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); | 44 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); |
45 | #endif | ||
43 | wl_list_init(&root_container.sway_root->drag_icons); | 46 | wl_list_init(&root_container.sway_root->drag_icons); |
44 | wl_signal_init(&root_container.sway_root->events.new_container); | 47 | wl_signal_init(&root_container.sway_root->events.new_container); |
48 | root_container.sway_root->scratchpad = create_list(); | ||
45 | 49 | ||
46 | root_container.sway_root->output_layout_change.notify = | 50 | root_container.sway_root->output_layout_change.notify = |
47 | output_layout_handle_change; | 51 | output_layout_handle_change; |
@@ -62,10 +66,9 @@ static int index_child(const struct sway_container *child) { | |||
62 | 66 | ||
63 | static void container_handle_fullscreen_reparent(struct sway_container *con, | 67 | static void container_handle_fullscreen_reparent(struct sway_container *con, |
64 | struct sway_container *old_parent) { | 68 | struct sway_container *old_parent) { |
65 | if (con->type != C_VIEW || !con->sway_view->is_fullscreen) { | 69 | if (!con->is_fullscreen) { |
66 | return; | 70 | return; |
67 | } | 71 | } |
68 | struct sway_view *view = con->sway_view; | ||
69 | struct sway_container *old_workspace = old_parent; | 72 | struct sway_container *old_workspace = old_parent; |
70 | if (old_workspace && old_workspace->type != C_WORKSPACE) { | 73 | if (old_workspace && old_workspace->type != C_WORKSPACE) { |
71 | old_workspace = container_parent(old_workspace, C_WORKSPACE); | 74 | old_workspace = container_parent(old_workspace, C_WORKSPACE); |
@@ -81,19 +84,27 @@ static void container_handle_fullscreen_reparent(struct sway_container *con, | |||
81 | 84 | ||
82 | // Mark the new workspace as fullscreen | 85 | // Mark the new workspace as fullscreen |
83 | if (new_workspace->sway_workspace->fullscreen) { | 86 | if (new_workspace->sway_workspace->fullscreen) { |
84 | view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false); | 87 | container_set_fullscreen( |
88 | new_workspace->sway_workspace->fullscreen, false); | ||
85 | } | 89 | } |
86 | new_workspace->sway_workspace->fullscreen = view; | 90 | new_workspace->sway_workspace->fullscreen = con; |
87 | // Resize view to new output dimensions | 91 | |
92 | // Resize container to new output dimensions | ||
88 | struct sway_container *output = new_workspace->parent; | 93 | struct sway_container *output = new_workspace->parent; |
89 | view->x = output->x; | ||
90 | view->y = output->y; | ||
91 | view->width = output->width; | ||
92 | view->height = output->height; | ||
93 | con->x = output->x; | 94 | con->x = output->x; |
94 | con->y = output->y; | 95 | con->y = output->y; |
95 | con->width = output->width; | 96 | con->width = output->width; |
96 | con->height = output->height; | 97 | con->height = output->height; |
98 | |||
99 | if (con->type == C_VIEW) { | ||
100 | struct sway_view *view = con->sway_view; | ||
101 | view->x = output->x; | ||
102 | view->y = output->y; | ||
103 | view->width = output->width; | ||
104 | view->height = output->height; | ||
105 | } else { | ||
106 | arrange_windows(new_workspace); | ||
107 | } | ||
97 | } | 108 | } |
98 | 109 | ||
99 | void container_insert_child(struct sway_container *parent, | 110 | void container_insert_child(struct sway_container *parent, |
@@ -135,10 +146,14 @@ void container_add_child(struct sway_container *parent, | |||
135 | list_add(parent->children, child); | 146 | list_add(parent->children, child); |
136 | child->parent = parent; | 147 | child->parent = parent; |
137 | container_handle_fullscreen_reparent(child, old_parent); | 148 | container_handle_fullscreen_reparent(child, old_parent); |
149 | if (old_parent) { | ||
150 | container_set_dirty(old_parent); | ||
151 | } | ||
152 | container_set_dirty(child); | ||
138 | } | 153 | } |
139 | 154 | ||
140 | struct sway_container *container_remove_child(struct sway_container *child) { | 155 | struct sway_container *container_remove_child(struct sway_container *child) { |
141 | if (child->type == C_VIEW && child->sway_view->is_fullscreen) { | 156 | if (child->is_fullscreen) { |
142 | struct sway_container *workspace = container_parent(child, C_WORKSPACE); | 157 | struct sway_container *workspace = container_parent(child, C_WORKSPACE); |
143 | workspace->sway_workspace->fullscreen = NULL; | 158 | workspace->sway_workspace->fullscreen = NULL; |
144 | } | 159 | } |
@@ -153,6 +168,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { | |||
153 | child->parent = NULL; | 168 | child->parent = NULL; |
154 | container_notify_subtree_changed(parent); | 169 | container_notify_subtree_changed(parent); |
155 | 170 | ||
171 | container_set_dirty(parent); | ||
172 | container_set_dirty(child); | ||
173 | |||
156 | return parent; | 174 | return parent; |
157 | } | 175 | } |
158 | 176 | ||
@@ -199,7 +217,9 @@ void container_move_to(struct sway_container *container, | |||
199 | container_sort_workspaces(new_parent); | 217 | container_sort_workspaces(new_parent); |
200 | seat_set_focus(seat, new_parent); | 218 | seat_set_focus(seat, new_parent); |
201 | workspace_output_raise_priority(container, old_parent, new_parent); | 219 | workspace_output_raise_priority(container, old_parent, new_parent); |
202 | ipc_event_workspace(container, NULL, "move"); | 220 | ipc_event_workspace(NULL, container, "move"); |
221 | } else if (container->type == C_VIEW) { | ||
222 | ipc_event_window(container, "move"); | ||
203 | } | 223 | } |
204 | container_notify_subtree_changed(old_parent); | 224 | container_notify_subtree_changed(old_parent); |
205 | container_notify_subtree_changed(new_parent); | 225 | container_notify_subtree_changed(new_parent); |
@@ -218,10 +238,10 @@ void container_move_to(struct sway_container *container, | |||
218 | if (focus_ws->type != C_WORKSPACE) { | 238 | if (focus_ws->type != C_WORKSPACE) { |
219 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | 239 | focus_ws = container_parent(focus_ws, C_WORKSPACE); |
220 | } | 240 | } |
221 | seat_set_focus(seat, | 241 | if (focus_ws == new_workspace) { |
222 | new_workspace->sway_workspace->fullscreen->swayc); | 242 | struct sway_container *new_focus = seat_get_focus_inactive(seat, |
223 | if (focus_ws != new_workspace) { | 243 | new_workspace->sway_workspace->fullscreen); |
224 | seat_set_focus(seat, focus); | 244 | seat_set_focus(seat, new_focus); |
225 | } | 245 | } |
226 | } | 246 | } |
227 | } | 247 | } |
@@ -364,10 +384,18 @@ void container_move(struct sway_container *container, | |||
364 | struct sway_container *sibling = NULL; | 384 | struct sway_container *sibling = NULL; |
365 | struct sway_container *current = container; | 385 | struct sway_container *current = container; |
366 | struct sway_container *parent = current->parent; | 386 | struct sway_container *parent = current->parent; |
387 | struct sway_container *top = &root_container; | ||
367 | 388 | ||
368 | // If moving a fullscreen view, only consider outputs | 389 | // If moving a fullscreen view, only consider outputs |
369 | if (container->type == C_VIEW && container->sway_view->is_fullscreen) { | 390 | if (container->is_fullscreen) { |
370 | current = container_parent(container, C_OUTPUT); | 391 | current = container_parent(container, C_OUTPUT); |
392 | } else if (container_is_fullscreen_or_child(container) || | ||
393 | container_is_floating_or_child(container)) { | ||
394 | // If we've fullscreened a split container, only allow the child to move | ||
395 | // around within the fullscreen parent. | ||
396 | // Same with floating a split container. | ||
397 | struct sway_container *ws = container_parent(container, C_WORKSPACE); | ||
398 | top = ws->sway_workspace->fullscreen; | ||
371 | } | 399 | } |
372 | 400 | ||
373 | struct sway_container *new_parent = container_flatten(parent); | 401 | struct sway_container *new_parent = container_flatten(parent); |
@@ -377,7 +405,7 @@ void container_move(struct sway_container *container, | |||
377 | } | 405 | } |
378 | 406 | ||
379 | while (!sibling) { | 407 | while (!sibling) { |
380 | if (current->type == C_ROOT) { | 408 | if (current == top) { |
381 | return; | 409 | return; |
382 | } | 410 | } |
383 | 411 | ||
@@ -441,8 +469,12 @@ void container_move(struct sway_container *container, | |||
441 | if ((index == parent->children->length - 1 && offs > 0) | 469 | if ((index == parent->children->length - 1 && offs > 0) |
442 | || (index == 0 && offs < 0)) { | 470 | || (index == 0 && offs < 0)) { |
443 | if (current->parent == container->parent) { | 471 | if (current->parent == container->parent) { |
444 | if (parent->layout == L_TABBED | 472 | if (parent->parent->layout == L_FLOATING) { |
445 | || parent->layout == L_STACKED) { | 473 | return; |
474 | } | ||
475 | if (!parent->is_fullscreen && | ||
476 | (parent->layout == L_TABBED || | ||
477 | parent->layout == L_STACKED)) { | ||
446 | move_out_of_tabs_stacks(container, current, | 478 | move_out_of_tabs_stacks(container, current, |
447 | move_dir, offs); | 479 | move_dir, offs); |
448 | return; | 480 | return; |
@@ -463,10 +495,14 @@ void container_move(struct sway_container *container, | |||
463 | sibling = parent->children->items[index + offs]; | 495 | sibling = parent->children->items[index + offs]; |
464 | wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); | 496 | wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); |
465 | } | 497 | } |
466 | } else if (parent->layout == L_TABBED | 498 | } else if (!parent->is_fullscreen && |
467 | || parent->layout == L_STACKED) { | 499 | parent->parent->layout != L_FLOATING && |
500 | (parent->layout == L_TABBED || | ||
501 | parent->layout == L_STACKED)) { | ||
468 | move_out_of_tabs_stacks(container, current, move_dir, offs); | 502 | move_out_of_tabs_stacks(container, current, move_dir, offs); |
469 | return; | 503 | return; |
504 | } else if (parent->parent->layout == L_FLOATING) { | ||
505 | return; | ||
470 | } else { | 506 | } else { |
471 | wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); | 507 | wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); |
472 | current = current->parent; | 508 | current = current->parent; |
@@ -544,6 +580,10 @@ void container_move(struct sway_container *container, | |||
544 | container_notify_subtree_changed(old_parent); | 580 | container_notify_subtree_changed(old_parent); |
545 | container_notify_subtree_changed(container->parent); | 581 | container_notify_subtree_changed(container->parent); |
546 | 582 | ||
583 | if (container->type == C_VIEW) { | ||
584 | ipc_event_window(container, "move"); | ||
585 | } | ||
586 | |||
547 | if (old_parent) { | 587 | if (old_parent) { |
548 | seat_set_focus(config->handler_context.seat, old_parent); | 588 | seat_set_focus(config->handler_context.seat, old_parent); |
549 | seat_set_focus(config->handler_context.seat, container); | 589 | seat_set_focus(config->handler_context.seat, container); |
@@ -558,10 +598,11 @@ void container_move(struct sway_container *container, | |||
558 | next_ws = container_parent(next_ws, C_WORKSPACE); | 598 | next_ws = container_parent(next_ws, C_WORKSPACE); |
559 | } | 599 | } |
560 | if (last_ws && next_ws && last_ws != next_ws) { | 600 | if (last_ws && next_ws && last_ws != next_ws) { |
561 | ipc_event_workspace(last_ws, container, "focus"); | 601 | ipc_event_workspace(last_ws, next_ws, "focus"); |
562 | workspace_detect_urgent(last_ws); | 602 | workspace_detect_urgent(last_ws); |
563 | workspace_detect_urgent(next_ws); | 603 | workspace_detect_urgent(next_ws); |
564 | } | 604 | } |
605 | container_end_mouse_operation(container); | ||
565 | } | 606 | } |
566 | 607 | ||
567 | enum sway_container_layout container_get_default_layout( | 608 | enum sway_container_layout container_get_default_layout( |
@@ -691,22 +732,18 @@ struct sway_container *container_get_in_direction( | |||
691 | enum movement_direction dir) { | 732 | enum movement_direction dir) { |
692 | struct sway_container *parent = container->parent; | 733 | struct sway_container *parent = container->parent; |
693 | 734 | ||
694 | if (container_is_floating(container)) { | 735 | if (dir == MOVE_CHILD) { |
695 | return NULL; | 736 | return seat_get_focus_inactive(seat, container); |
696 | } | 737 | } |
697 | 738 | if (container->is_fullscreen) { | |
698 | if (container->type == C_VIEW && container->sway_view->is_fullscreen) { | 739 | if (dir == MOVE_PARENT) { |
699 | if (dir == MOVE_PARENT || dir == MOVE_CHILD) { | ||
700 | return NULL; | 740 | return NULL; |
701 | } | 741 | } |
702 | container = container_parent(container, C_OUTPUT); | 742 | container = container_parent(container, C_OUTPUT); |
703 | parent = container->parent; | 743 | parent = container->parent; |
704 | } else { | 744 | } else { |
705 | if (dir == MOVE_CHILD) { | ||
706 | return seat_get_focus_inactive(seat, container); | ||
707 | } | ||
708 | if (dir == MOVE_PARENT) { | 745 | if (dir == MOVE_PARENT) { |
709 | if (parent->type == C_OUTPUT) { | 746 | if (parent->type == C_OUTPUT || container_is_floating(container)) { |
710 | return NULL; | 747 | return NULL; |
711 | } else { | 748 | } else { |
712 | return parent; | 749 | return parent; |
@@ -755,7 +792,8 @@ struct sway_container *container_get_in_direction( | |||
755 | } | 792 | } |
756 | sway_assert(next_workspace, "Next container has no workspace"); | 793 | sway_assert(next_workspace, "Next container has no workspace"); |
757 | if (next_workspace->sway_workspace->fullscreen) { | 794 | if (next_workspace->sway_workspace->fullscreen) { |
758 | return next_workspace->sway_workspace->fullscreen->swayc; | 795 | return seat_get_focus_inactive(seat, |
796 | next_workspace->sway_workspace->fullscreen); | ||
759 | } | 797 | } |
760 | if (next->children && next->children->length) { | 798 | if (next->children && next->children->length) { |
761 | // TODO consider floating children as well | 799 | // TODO consider floating children as well |
@@ -963,13 +1001,13 @@ static void swap_focus(struct sway_container *con1, | |||
963 | if (focus == con1 && (con2->parent->layout == L_TABBED | 1001 | if (focus == con1 && (con2->parent->layout == L_TABBED |
964 | || con2->parent->layout == L_STACKED)) { | 1002 | || con2->parent->layout == L_STACKED)) { |
965 | if (workspace_is_visible(ws2)) { | 1003 | if (workspace_is_visible(ws2)) { |
966 | seat_set_focus_warp(seat, con2, false); | 1004 | seat_set_focus_warp(seat, con2, false, true); |
967 | } | 1005 | } |
968 | seat_set_focus(seat, ws1 != ws2 ? con2 : con1); | 1006 | seat_set_focus(seat, ws1 != ws2 ? con2 : con1); |
969 | } else if (focus == con2 && (con1->parent->layout == L_TABBED | 1007 | } else if (focus == con2 && (con1->parent->layout == L_TABBED |
970 | || con1->parent->layout == L_STACKED)) { | 1008 | || con1->parent->layout == L_STACKED)) { |
971 | if (workspace_is_visible(ws1)) { | 1009 | if (workspace_is_visible(ws1)) { |
972 | seat_set_focus_warp(seat, con1, false); | 1010 | seat_set_focus_warp(seat, con1, false, true); |
973 | } | 1011 | } |
974 | seat_set_focus(seat, ws1 != ws2 ? con1 : con2); | 1012 | seat_set_focus(seat, ws1 != ws2 ? con1 : con2); |
975 | } else if (ws1 != ws2) { | 1013 | } else if (ws1 != ws2) { |
@@ -1002,13 +1040,13 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
1002 | 1040 | ||
1003 | wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); | 1041 | wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); |
1004 | 1042 | ||
1005 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; | 1043 | int fs1 = con1->is_fullscreen; |
1006 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; | 1044 | int fs2 = con2->is_fullscreen; |
1007 | if (fs1) { | 1045 | if (fs1) { |
1008 | view_set_fullscreen(con1->sway_view, false); | 1046 | container_set_fullscreen(con1, false); |
1009 | } | 1047 | } |
1010 | if (fs2) { | 1048 | if (fs2) { |
1011 | view_set_fullscreen(con2->sway_view, false); | 1049 | container_set_fullscreen(con2, false); |
1012 | } | 1050 | } |
1013 | 1051 | ||
1014 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | 1052 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); |
@@ -1041,10 +1079,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
1041 | prev_workspace_name = stored_prev_name; | 1079 | prev_workspace_name = stored_prev_name; |
1042 | } | 1080 | } |
1043 | 1081 | ||
1044 | if (fs1 && con2->type == C_VIEW) { | 1082 | if (fs1) { |
1045 | view_set_fullscreen(con2->sway_view, true); | 1083 | container_set_fullscreen(con2, true); |
1046 | } | 1084 | } |
1047 | if (fs2 && con1->type == C_VIEW) { | 1085 | if (fs2) { |
1048 | view_set_fullscreen(con1->sway_view, true); | 1086 | container_set_fullscreen(con1, true); |
1049 | } | 1087 | } |
1050 | } | 1088 | } |
diff --git a/sway/tree/output.c b/sway/tree/output.c index da535c18..31e3bf9b 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) { | |||
22 | if (highest == output) { | 22 | if (highest == output) { |
23 | container_remove_child(ws); | 23 | container_remove_child(ws); |
24 | container_add_child(output, ws); | 24 | container_add_child(output, ws); |
25 | ipc_event_workspace(ws, NULL, "move"); | 25 | ipc_event_workspace(NULL, ws, "move"); |
26 | j--; | 26 | j--; |
27 | } | 27 | } |
28 | } | 28 | } |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7881e6d7..97318daa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -2,7 +2,12 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <wayland-server.h> | 3 | #include <wayland-server.h> |
4 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
5 | #include <wlr/types/wlr_buffer.h> | ||
5 | #include <wlr/types/wlr_output_layout.h> | 6 | #include <wlr/types/wlr_output_layout.h> |
7 | #include "config.h" | ||
8 | #ifdef HAVE_XWAYLAND | ||
9 | #include <wlr/xwayland.h> | ||
10 | #endif | ||
6 | #include "list.h" | 11 | #include "list.h" |
7 | #include "log.h" | 12 | #include "log.h" |
8 | #include "sway/criteria.h" | 13 | #include "sway/criteria.h" |
@@ -107,14 +112,14 @@ const char *view_get_instance(struct sway_view *view) { | |||
107 | } | 112 | } |
108 | return NULL; | 113 | return NULL; |
109 | } | 114 | } |
110 | 115 | #ifdef HAVE_XWAYLAND | |
111 | uint32_t view_get_x11_window_id(struct sway_view *view) { | 116 | uint32_t view_get_x11_window_id(struct sway_view *view) { |
112 | if (view->impl->get_int_prop) { | 117 | if (view->impl->get_int_prop) { |
113 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); | 118 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); |
114 | } | 119 | } |
115 | return 0; | 120 | return 0; |
116 | } | 121 | } |
117 | 122 | #endif | |
118 | const char *view_get_window_role(struct sway_view *view) { | 123 | const char *view_get_window_role(struct sway_view *view) { |
119 | if (view->impl->get_string_prop) { | 124 | if (view->impl->get_string_prop) { |
120 | return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); | 125 | return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); |
@@ -135,12 +140,27 @@ const char *view_get_shell(struct sway_view *view) { | |||
135 | return "xdg_shell_v6"; | 140 | return "xdg_shell_v6"; |
136 | case SWAY_VIEW_XDG_SHELL: | 141 | case SWAY_VIEW_XDG_SHELL: |
137 | return "xdg_shell"; | 142 | return "xdg_shell"; |
143 | #ifdef HAVE_XWAYLAND | ||
138 | case SWAY_VIEW_XWAYLAND: | 144 | case SWAY_VIEW_XWAYLAND: |
139 | return "xwayland"; | 145 | return "xwayland"; |
146 | #endif | ||
140 | } | 147 | } |
141 | return "unknown"; | 148 | return "unknown"; |
142 | } | 149 | } |
143 | 150 | ||
151 | void view_get_constraints(struct sway_view *view, double *min_width, | ||
152 | double *max_width, double *min_height, double *max_height) { | ||
153 | if (view->impl->get_constraints) { | ||
154 | view->impl->get_constraints(view, | ||
155 | min_width, max_width, min_height, max_height); | ||
156 | } else { | ||
157 | *min_width = DBL_MIN; | ||
158 | *max_width = DBL_MAX; | ||
159 | *min_height = DBL_MIN; | ||
160 | *max_height = DBL_MAX; | ||
161 | } | ||
162 | } | ||
163 | |||
144 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | 164 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, |
145 | int height) { | 165 | int height) { |
146 | if (view->impl->configure) { | 166 | if (view->impl->configure) { |
@@ -149,55 +169,6 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
149 | return 0; | 169 | return 0; |
150 | } | 170 | } |
151 | 171 | ||
152 | void view_init_floating(struct sway_view *view) { | ||
153 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
154 | int min_width, min_height; | ||
155 | int max_width, max_height; | ||
156 | |||
157 | if (config->floating_minimum_width == -1) { // no minimum | ||
158 | min_width = 0; | ||
159 | } else if (config->floating_minimum_width == 0) { // automatic | ||
160 | min_width = 75; | ||
161 | } else { | ||
162 | min_width = config->floating_minimum_width; | ||
163 | } | ||
164 | |||
165 | if (config->floating_minimum_height == -1) { // no minimum | ||
166 | min_height = 0; | ||
167 | } else if (config->floating_minimum_height == 0) { // automatic | ||
168 | min_height = 50; | ||
169 | } else { | ||
170 | min_height = config->floating_minimum_height; | ||
171 | } | ||
172 | |||
173 | if (config->floating_maximum_width == -1) { // no maximum | ||
174 | max_width = INT_MAX; | ||
175 | } else if (config->floating_maximum_width == 0) { // automatic | ||
176 | max_width = ws->width * 0.6666; | ||
177 | } else { | ||
178 | max_width = config->floating_maximum_width; | ||
179 | } | ||
180 | |||
181 | if (config->floating_maximum_height == -1) { // no maximum | ||
182 | max_height = INT_MAX; | ||
183 | } else if (config->floating_maximum_height == 0) { // automatic | ||
184 | max_height = ws->height * 0.6666; | ||
185 | } else { | ||
186 | max_height = config->floating_maximum_height; | ||
187 | } | ||
188 | |||
189 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
190 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
191 | view->x = ws->x + (ws->width - view->width) / 2; | ||
192 | view->y = ws->y + (ws->height - view->height) / 2; | ||
193 | |||
194 | // If the view's border is B_NONE then these properties are ignored. | ||
195 | view->border_top = view->border_bottom = true; | ||
196 | view->border_left = view->border_right = true; | ||
197 | |||
198 | container_set_geometry_from_floating_view(view->swayc); | ||
199 | } | ||
200 | |||
201 | void view_autoconfigure(struct sway_view *view) { | 172 | void view_autoconfigure(struct sway_view *view) { |
202 | if (!sway_assert(view->swayc, | 173 | if (!sway_assert(view->swayc, |
203 | "Called view_autoconfigure() on a view without a swayc")) { | 174 | "Called view_autoconfigure() on a view without a swayc")) { |
@@ -206,7 +177,7 @@ void view_autoconfigure(struct sway_view *view) { | |||
206 | 177 | ||
207 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 178 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
208 | 179 | ||
209 | if (view->is_fullscreen) { | 180 | if (view->swayc->is_fullscreen) { |
210 | view->x = output->x; | 181 | view->x = output->x; |
211 | view->y = output->y; | 182 | view->y = output->y; |
212 | view->width = output->width; | 183 | view->width = output->width; |
@@ -214,10 +185,6 @@ void view_autoconfigure(struct sway_view *view) { | |||
214 | return; | 185 | return; |
215 | } | 186 | } |
216 | 187 | ||
217 | if (container_is_floating(view->swayc)) { | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 188 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
222 | 189 | ||
223 | int other_views = 0; | 190 | int other_views = 0; |
@@ -330,72 +297,18 @@ void view_set_tiled(struct sway_view *view, bool tiled) { | |||
330 | } | 297 | } |
331 | } | 298 | } |
332 | 299 | ||
333 | void view_set_fullscreen(struct sway_view *view, bool fullscreen) { | ||
334 | if (view->is_fullscreen == fullscreen) { | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | struct sway_container *workspace = | ||
339 | container_parent(view->swayc, C_WORKSPACE); | ||
340 | |||
341 | if (view->impl->set_fullscreen) { | ||
342 | view->impl->set_fullscreen(view, fullscreen); | ||
343 | } | ||
344 | |||
345 | view->is_fullscreen = fullscreen; | ||
346 | |||
347 | if (fullscreen) { | ||
348 | if (workspace->sway_workspace->fullscreen) { | ||
349 | view_set_fullscreen(workspace->sway_workspace->fullscreen, false); | ||
350 | } | ||
351 | workspace->sway_workspace->fullscreen = view; | ||
352 | view->saved_x = view->x; | ||
353 | view->saved_y = view->y; | ||
354 | view->saved_width = view->width; | ||
355 | view->saved_height = view->height; | ||
356 | view->swayc->saved_x = view->swayc->x; | ||
357 | view->swayc->saved_y = view->swayc->y; | ||
358 | view->swayc->saved_width = view->swayc->width; | ||
359 | view->swayc->saved_height = view->swayc->height; | ||
360 | |||
361 | struct sway_seat *seat; | ||
362 | struct sway_container *focus, *focus_ws; | ||
363 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
364 | focus = seat_get_focus(seat); | ||
365 | if (focus) { | ||
366 | focus_ws = focus; | ||
367 | if (focus && focus_ws->type != C_WORKSPACE) { | ||
368 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | ||
369 | } | ||
370 | seat_set_focus(seat, view->swayc); | ||
371 | if (focus_ws != workspace) { | ||
372 | seat_set_focus(seat, focus); | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | } else { | ||
377 | workspace->sway_workspace->fullscreen = NULL; | ||
378 | if (container_is_floating(view->swayc)) { | ||
379 | view->x = view->saved_x; | ||
380 | view->y = view->saved_y; | ||
381 | view->width = view->saved_width; | ||
382 | view->height = view->saved_height; | ||
383 | container_set_geometry_from_floating_view(view->swayc); | ||
384 | } else { | ||
385 | view->swayc->width = view->swayc->saved_width; | ||
386 | view->swayc->height = view->swayc->saved_height; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | ipc_event_window(view->swayc, "fullscreen_mode"); | ||
391 | } | ||
392 | |||
393 | void view_close(struct sway_view *view) { | 300 | void view_close(struct sway_view *view) { |
394 | if (view->impl->close) { | 301 | if (view->impl->close) { |
395 | view->impl->close(view); | 302 | view->impl->close(view); |
396 | } | 303 | } |
397 | } | 304 | } |
398 | 305 | ||
306 | void view_close_popups(struct sway_view *view) { | ||
307 | if (view->impl->close_popups) { | ||
308 | view->impl->close_popups(view); | ||
309 | } | ||
310 | } | ||
311 | |||
399 | void view_damage_from(struct sway_view *view) { | 312 | void view_damage_from(struct sway_view *view) { |
400 | for (int i = 0; i < root_container.children->length; ++i) { | 313 | for (int i = 0; i < root_container.children->length; ++i) { |
401 | struct sway_container *cont = root_container.children->items[i]; | 314 | struct sway_container *cont = root_container.children->items[i]; |
@@ -426,6 +339,16 @@ void view_for_each_surface(struct sway_view *view, | |||
426 | } | 339 | } |
427 | } | 340 | } |
428 | 341 | ||
342 | void view_for_each_popup(struct sway_view *view, | ||
343 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
344 | if (!view->surface) { | ||
345 | return; | ||
346 | } | ||
347 | if (view->impl->for_each_popup) { | ||
348 | view->impl->for_each_popup(view, iterator, user_data); | ||
349 | } | ||
350 | } | ||
351 | |||
429 | static void view_subsurface_create(struct sway_view *view, | 352 | static void view_subsurface_create(struct sway_view *view, |
430 | struct wlr_subsurface *subsurface); | 353 | struct wlr_subsurface *subsurface); |
431 | 354 | ||
@@ -521,12 +444,82 @@ void view_execute_criteria(struct sway_view *view) { | |||
521 | seat_set_focus(seat, prior_focus); | 444 | seat_set_focus(seat, prior_focus); |
522 | } | 445 | } |
523 | 446 | ||
447 | static struct sway_container *select_workspace(struct sway_view *view) { | ||
448 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
449 | |||
450 | // Check if there's any `assign` criteria for the view | ||
451 | list_t *criterias = criteria_for_view(view, | ||
452 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); | ||
453 | struct sway_container *ws = NULL; | ||
454 | for (int i = 0; i < criterias->length; ++i) { | ||
455 | struct criteria *criteria = criterias->items[i]; | ||
456 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | ||
457 | ws = workspace_by_name(criteria->target); | ||
458 | if (!ws) { | ||
459 | ws = workspace_create(NULL, criteria->target); | ||
460 | } | ||
461 | break; | ||
462 | } else { | ||
463 | // CT_ASSIGN_OUTPUT | ||
464 | struct sway_container *output = output_by_name(criteria->target); | ||
465 | if (output) { | ||
466 | ws = seat_get_active_child(seat, output); | ||
467 | break; | ||
468 | } | ||
469 | } | ||
470 | } | ||
471 | list_free(criterias); | ||
472 | if (ws) { | ||
473 | return ws; | ||
474 | } | ||
475 | |||
476 | // Check if there's a PID mapping | ||
477 | pid_t pid; | ||
478 | #ifdef HAVE_XWAYLAND | ||
479 | if (view->type == SWAY_VIEW_XWAYLAND) { | ||
480 | struct wlr_xwayland_surface *surf = | ||
481 | wlr_xwayland_surface_from_wlr_surface(view->surface); | ||
482 | pid = surf->pid; | ||
483 | } else { | ||
484 | struct wl_client *client = | ||
485 | wl_resource_get_client(view->surface->resource); | ||
486 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
487 | } | ||
488 | #else | ||
489 | struct wl_client *client = | ||
490 | wl_resource_get_client(view->surface->resource); | ||
491 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
492 | #endif | ||
493 | ws = workspace_for_pid(pid); | ||
494 | if (ws) { | ||
495 | return ws; | ||
496 | } | ||
497 | |||
498 | // Use the focused workspace | ||
499 | ws = seat_get_focus_inactive(seat, &root_container); | ||
500 | if (ws->type != C_WORKSPACE) { | ||
501 | ws = container_parent(ws, C_WORKSPACE); | ||
502 | } | ||
503 | return ws; | ||
504 | } | ||
505 | |||
524 | static bool should_focus(struct sway_view *view) { | 506 | static bool should_focus(struct sway_view *view) { |
507 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
508 | struct sway_container *prev_focus = | ||
509 | seat_get_focus_inactive(seat, &root_container); | ||
510 | struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? | ||
511 | prev_focus : container_parent(prev_focus, C_WORKSPACE); | ||
512 | struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); | ||
513 | |||
514 | // Views can only take focus if they are mapped into the active workspace | ||
515 | if (prev_ws != map_ws) { | ||
516 | return false; | ||
517 | } | ||
518 | |||
525 | // If the view is the only one in the focused workspace, it'll get focus | 519 | // If the view is the only one in the focused workspace, it'll get focus |
526 | // regardless of any no_focus criteria. | 520 | // regardless of any no_focus criteria. |
527 | struct sway_container *parent = view->swayc->parent; | 521 | struct sway_container *parent = view->swayc->parent; |
528 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 522 | if (parent->type == C_WORKSPACE && prev_focus == parent) { |
529 | if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { | ||
530 | size_t num_children = parent->children->length + | 523 | size_t num_children = parent->children->length + |
531 | parent->sway_workspace->floating->children->length; | 524 | parent->sway_workspace->floating->children->length; |
532 | if (num_children == 1) { | 525 | if (num_children == 1) { |
@@ -545,42 +538,19 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
545 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | 538 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { |
546 | return; | 539 | return; |
547 | } | 540 | } |
541 | view->surface = wlr_surface; | ||
548 | 542 | ||
549 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 543 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
550 | struct sway_container *focus = | 544 | struct sway_container *ws = select_workspace(view); |
551 | seat_get_focus_inactive(seat, &root_container); | 545 | struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); |
552 | struct sway_container *cont = NULL; | ||
553 | 546 | ||
554 | // Check if there's any `assign` criteria for the view | ||
555 | list_t *criterias = criteria_for_view(view, | ||
556 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); | ||
557 | struct sway_container *workspace = NULL; | ||
558 | if (criterias->length) { | ||
559 | struct criteria *criteria = criterias->items[0]; | ||
560 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | ||
561 | workspace = workspace_by_name(criteria->target); | ||
562 | if (!workspace) { | ||
563 | workspace = workspace_create(NULL, criteria->target); | ||
564 | } | ||
565 | focus = seat_get_focus_inactive(seat, workspace); | ||
566 | } else { | ||
567 | // CT_ASSIGN_OUTPUT | ||
568 | struct sway_container *output = output_by_name(criteria->target); | ||
569 | if (output) { | ||
570 | focus = seat_get_focus_inactive(seat, output); | ||
571 | } | ||
572 | } | ||
573 | } | ||
574 | // If we're about to launch the view into the floating container, then | 547 | // If we're about to launch the view into the floating container, then |
575 | // launch it as a tiled view in the root of the workspace instead. | 548 | // launch it as a tiled view in the root of the workspace instead. |
576 | if (container_is_floating(focus)) { | 549 | if (container_is_floating(target_sibling)) { |
577 | focus = focus->parent->parent; | 550 | target_sibling = target_sibling->parent->parent; |
578 | } | 551 | } |
579 | list_free(criterias); | ||
580 | cont = container_view_create(focus, view); | ||
581 | 552 | ||
582 | view->surface = wlr_surface; | 553 | view->swayc = container_view_create(target_sibling, view); |
583 | view->swayc = cont; | ||
584 | 554 | ||
585 | view_init_subsurfaces(view, wlr_surface); | 555 | view_init_subsurfaces(view, wlr_surface); |
586 | wl_signal_add(&wlr_surface->events.new_subsurface, | 556 | wl_signal_add(&wlr_surface->events.new_subsurface, |
@@ -601,10 +571,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
601 | } | 571 | } |
602 | 572 | ||
603 | if (should_focus(view)) { | 573 | if (should_focus(view)) { |
604 | input_manager_set_focus(input_manager, cont); | 574 | input_manager_set_focus(input_manager, view->swayc); |
605 | if (workspace) { | ||
606 | workspace_switch(workspace); | ||
607 | } | ||
608 | } | 575 | } |
609 | 576 | ||
610 | view_update_title(view, false); | 577 | view_update_title(view, false); |
@@ -628,10 +595,8 @@ void view_unmap(struct sway_view *view) { | |||
628 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 595 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
629 | 596 | ||
630 | struct sway_container *parent; | 597 | struct sway_container *parent; |
631 | if (view->is_fullscreen) { | 598 | if (container_is_fullscreen_or_child(view->swayc)) { |
632 | ws->sway_workspace->fullscreen = NULL; | ||
633 | parent = container_destroy(view->swayc); | 599 | parent = container_destroy(view->swayc); |
634 | |||
635 | arrange_windows(ws->parent); | 600 | arrange_windows(ws->parent); |
636 | } else { | 601 | } else { |
637 | parent = container_destroy(view->swayc); | 602 | parent = container_destroy(view->swayc); |
@@ -784,11 +749,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | |||
784 | wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); | 749 | wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); |
785 | return view_from_wlr_xdg_surface_v6(xdg_surface_v6); | 750 | return view_from_wlr_xdg_surface_v6(xdg_surface_v6); |
786 | } | 751 | } |
752 | #ifdef HAVE_XWAYLAND | ||
787 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 753 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { |
788 | struct wlr_xwayland_surface *xsurface = | 754 | struct wlr_xwayland_surface *xsurface = |
789 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 755 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); |
790 | return view_from_wlr_xwayland_surface(xsurface); | 756 | return view_from_wlr_xwayland_surface(xsurface); |
791 | } | 757 | } |
758 | #endif | ||
792 | if (wlr_surface_is_subsurface(wlr_surface)) { | 759 | if (wlr_surface_is_subsurface(wlr_surface)) { |
793 | struct wlr_subsurface *subsurface = | 760 | struct wlr_subsurface *subsurface = |
794 | wlr_subsurface_from_wlr_surface(wlr_surface); | 761 | wlr_subsurface_from_wlr_surface(wlr_surface); |
@@ -915,6 +882,8 @@ void view_update_title(struct sway_view *view, bool force) { | |||
915 | 882 | ||
916 | // Update title after the global font height is updated | 883 | // Update title after the global font height is updated |
917 | container_update_title_textures(view->swayc); | 884 | container_update_title_textures(view->swayc); |
885 | |||
886 | ipc_event_window(view->swayc, "title"); | ||
918 | } | 887 | } |
919 | 888 | ||
920 | static bool find_by_mark_iterator(struct sway_container *con, | 889 | static bool find_by_mark_iterator(struct sway_container *con, |
@@ -937,6 +906,7 @@ bool view_find_and_unmark(char *mark) { | |||
937 | free(view_mark); | 906 | free(view_mark); |
938 | list_del(view->marks, i); | 907 | list_del(view->marks, i); |
939 | view_update_marks_textures(view); | 908 | view_update_marks_textures(view); |
909 | ipc_event_window(container, "mark"); | ||
940 | return true; | 910 | return true; |
941 | } | 911 | } |
942 | } | 912 | } |
@@ -944,11 +914,10 @@ bool view_find_and_unmark(char *mark) { | |||
944 | } | 914 | } |
945 | 915 | ||
946 | void view_clear_marks(struct sway_view *view) { | 916 | void view_clear_marks(struct sway_view *view) { |
947 | for (int i = 0; i < view->marks->length; ++i) { | 917 | while (view->marks->length) { |
948 | free(view->marks->items[i]); | 918 | list_del(view->marks, 0); |
919 | ipc_event_window(view->swayc, "mark"); | ||
949 | } | 920 | } |
950 | list_free(view->marks); | ||
951 | view->marks = create_list(); | ||
952 | } | 921 | } |
953 | 922 | ||
954 | bool view_has_mark(struct sway_view *view, char *mark) { | 923 | bool view_has_mark(struct sway_view *view, char *mark) { |
@@ -961,6 +930,11 @@ bool view_has_mark(struct sway_view *view, char *mark) { | |||
961 | return false; | 930 | return false; |
962 | } | 931 | } |
963 | 932 | ||
933 | void view_add_mark(struct sway_view *view, char *mark) { | ||
934 | list_add(view->marks, strdup(mark)); | ||
935 | ipc_event_window(view->swayc, "mark"); | ||
936 | } | ||
937 | |||
964 | static void update_marks_texture(struct sway_view *view, | 938 | static void update_marks_texture(struct sway_view *view, |
965 | struct wlr_texture **texture, struct border_colors *class) { | 939 | struct wlr_texture **texture, struct border_colors *class) { |
966 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 940 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
@@ -1055,6 +1029,9 @@ bool view_is_visible(struct sway_view *view) { | |||
1055 | } | 1029 | } |
1056 | struct sway_container *workspace = | 1030 | struct sway_container *workspace = |
1057 | container_parent(view->swayc, C_WORKSPACE); | 1031 | container_parent(view->swayc, C_WORKSPACE); |
1032 | if (!workspace) { | ||
1033 | return false; | ||
1034 | } | ||
1058 | // Determine if view is nested inside a floating container which is sticky. | 1035 | // Determine if view is nested inside a floating container which is sticky. |
1059 | // A simple floating view will have this ancestry: | 1036 | // A simple floating view will have this ancestry: |
1060 | // C_VIEW -> floating -> workspace | 1037 | // C_VIEW -> floating -> workspace |
@@ -1079,7 +1056,8 @@ bool view_is_visible(struct sway_view *view) { | |||
1079 | container = container->parent; | 1056 | container = container->parent; |
1080 | } | 1057 | } |
1081 | // Check view isn't hidden by another fullscreen view | 1058 | // Check view isn't hidden by another fullscreen view |
1082 | if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { | 1059 | if (workspace->sway_workspace->fullscreen && |
1060 | !container_is_fullscreen_or_child(view->swayc)) { | ||
1083 | return false; | 1061 | return false; |
1084 | } | 1062 | } |
1085 | // Check the workspace is visible | 1063 | // Check the workspace is visible |
@@ -1117,3 +1095,22 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1117 | bool view_is_urgent(struct sway_view *view) { | 1095 | bool view_is_urgent(struct sway_view *view) { |
1118 | return view->urgent.tv_sec || view->urgent.tv_nsec; | 1096 | return view->urgent.tv_sec || view->urgent.tv_nsec; |
1119 | } | 1097 | } |
1098 | |||
1099 | void view_remove_saved_buffer(struct sway_view *view) { | ||
1100 | if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) { | ||
1101 | return; | ||
1102 | } | ||
1103 | wlr_buffer_unref(view->saved_buffer); | ||
1104 | view->saved_buffer = NULL; | ||
1105 | } | ||
1106 | |||
1107 | void view_save_buffer(struct sway_view *view) { | ||
1108 | if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) { | ||
1109 | view_remove_saved_buffer(view); | ||
1110 | } | ||
1111 | if (view->surface && wlr_surface_has_buffer(view->surface)) { | ||
1112 | view->saved_buffer = wlr_buffer_ref(view->surface->buffer); | ||
1113 | view->saved_buffer_width = view->surface->current.width; | ||
1114 | view->saved_buffer_height = view->surface->current.height; | ||
1115 | } | ||
1116 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 622f01ec..588e2aae 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "sway/input/input-manager.h" | 9 | #include "sway/input/input-manager.h" |
10 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
11 | #include "sway/ipc-server.h" | 11 | #include "sway/ipc-server.h" |
12 | #include "sway/output.h" | ||
12 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
13 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
14 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
@@ -107,96 +108,100 @@ static bool workspace_valid_on_output(const char *output_name, | |||
107 | return true; | 108 | return true; |
108 | } | 109 | } |
109 | 110 | ||
110 | char *workspace_next_name(const char *output_name) { | 111 | static void workspace_name_from_binding(const struct sway_binding * binding, |
111 | wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", | 112 | const char* output_name, int *min_order, char **earliest_name) { |
112 | output_name); | 113 | char *cmdlist = strdup(binding->command); |
113 | // Scan all workspace bindings to find the next available workspace name, | 114 | char *dup = cmdlist; |
114 | // if none are found/available then default to a number | 115 | char *name = NULL; |
115 | struct sway_mode *mode = config->current_mode; | ||
116 | 116 | ||
117 | // TODO: iterate over keycode bindings too | 117 | // workspace n |
118 | int order = INT_MAX; | 118 | char *cmd = argsep(&cmdlist, " "); |
119 | char *target = NULL; | 119 | if (cmdlist) { |
120 | for (int i = 0; i < mode->keysym_bindings->length; ++i) { | 120 | name = argsep(&cmdlist, ",;"); |
121 | struct sway_binding *binding = mode->keysym_bindings->items[i]; | 121 | } |
122 | char *cmdlist = strdup(binding->command); | ||
123 | char *dup = cmdlist; | ||
124 | char *name = NULL; | ||
125 | |||
126 | // workspace n | ||
127 | char *cmd = argsep(&cmdlist, " "); | ||
128 | if (cmdlist) { | ||
129 | name = argsep(&cmdlist, ",;"); | ||
130 | } | ||
131 | 122 | ||
132 | if (strcmp("workspace", cmd) == 0 && name) { | 123 | if (strcmp("workspace", cmd) == 0 && name) { |
133 | char *_target = strdup(name); | 124 | char *_target = strdup(name); |
134 | _target = do_var_replacement(_target); | 125 | _target = do_var_replacement(_target); |
135 | strip_quotes(_target); | 126 | strip_quotes(_target); |
136 | while (isspace(*_target)) { | 127 | wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", |
137 | memmove(_target, _target+1, strlen(_target+1)); | 128 | _target); |
138 | } | ||
139 | wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", | ||
140 | _target); | ||
141 | 129 | ||
142 | // Make sure that the command references an actual workspace | 130 | // Make sure that the command references an actual workspace |
143 | // not a command about workspaces | 131 | // not a command about workspaces |
144 | if (strcmp(_target, "next") == 0 || | 132 | if (strcmp(_target, "next") == 0 || |
145 | strcmp(_target, "prev") == 0 || | 133 | strcmp(_target, "prev") == 0 || |
146 | strcmp(_target, "next_on_output") == 0 || | 134 | strcmp(_target, "next_on_output") == 0 || |
147 | strcmp(_target, "prev_on_output") == 0 || | 135 | strcmp(_target, "prev_on_output") == 0 || |
148 | strcmp(_target, "number") == 0 || | 136 | strcmp(_target, "number") == 0 || |
149 | strcmp(_target, "back_and_forth") == 0 || | 137 | strcmp(_target, "back_and_forth") == 0 || |
150 | strcmp(_target, "current") == 0) | 138 | strcmp(_target, "current") == 0) { |
151 | { | 139 | free(_target); |
152 | free(_target); | 140 | free(dup); |
153 | free(dup); | 141 | return; |
154 | continue; | 142 | } |
155 | } | ||
156 | |||
157 | // If the command is workspace number <name>, isolate the name | ||
158 | if (strncmp(_target, "number ", strlen("number ")) == 0) { | ||
159 | size_t length = strlen(_target) - strlen("number ") + 1; | ||
160 | char *temp = malloc(length); | ||
161 | strncpy(temp, _target + strlen("number "), length - 1); | ||
162 | temp[length - 1] = '\0'; | ||
163 | free(_target); | ||
164 | _target = temp; | ||
165 | wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); | ||
166 | |||
167 | // Make sure the workspace number doesn't already exist | ||
168 | if (workspace_by_number(_target)) { | ||
169 | free(_target); | ||
170 | free(dup); | ||
171 | continue; | ||
172 | } | ||
173 | } | ||
174 | 143 | ||
175 | // Make sure that the workspace doesn't already exist | 144 | // If the command is workspace number <name>, isolate the name |
176 | if (workspace_by_name(_target)) { | 145 | if (strncmp(_target, "number ", strlen("number ")) == 0) { |
146 | size_t length = strlen(_target) - strlen("number ") + 1; | ||
147 | char *temp = malloc(length); | ||
148 | strncpy(temp, _target + strlen("number "), length - 1); | ||
149 | temp[length - 1] = '\0'; | ||
150 | free(_target); | ||
151 | _target = temp; | ||
152 | wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); | ||
153 | |||
154 | // Make sure the workspace number doesn't already exist | ||
155 | if (workspace_by_number(_target)) { | ||
177 | free(_target); | 156 | free(_target); |
178 | free(dup); | 157 | free(dup); |
179 | continue; | 158 | return; |
180 | } | 159 | } |
160 | } | ||
181 | 161 | ||
182 | // make sure that the workspace can appear on the given | 162 | // Make sure that the workspace doesn't already exist |
183 | // output | 163 | if (workspace_by_name(_target)) { |
184 | if (!workspace_valid_on_output(output_name, _target)) { | 164 | free(_target); |
185 | free(_target); | 165 | free(dup); |
186 | free(dup); | 166 | return; |
187 | continue; | 167 | } |
188 | } | ||
189 | 168 | ||
190 | if (binding->order < order) { | 169 | // make sure that the workspace can appear on the given |
191 | order = binding->order; | 170 | // output |
192 | free(target); | 171 | if (!workspace_valid_on_output(output_name, _target)) { |
193 | target = _target; | 172 | free(_target); |
194 | wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); | 173 | free(dup); |
195 | } else { | 174 | return; |
196 | free(_target); | ||
197 | } | ||
198 | } | 175 | } |
199 | free(dup); | 176 | |
177 | if (binding->order < *min_order) { | ||
178 | *min_order = binding->order; | ||
179 | free(*earliest_name); | ||
180 | *earliest_name = _target; | ||
181 | wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); | ||
182 | } else { | ||
183 | free(_target); | ||
184 | } | ||
185 | } | ||
186 | free(dup); | ||
187 | } | ||
188 | |||
189 | char *workspace_next_name(const char *output_name) { | ||
190 | wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", | ||
191 | output_name); | ||
192 | // Scan all workspace bindings to find the next available workspace name, | ||
193 | // if none are found/available then default to a number | ||
194 | struct sway_mode *mode = config->current_mode; | ||
195 | |||
196 | int order = INT_MAX; | ||
197 | char *target = NULL; | ||
198 | for (int i = 0; i < mode->keysym_bindings->length; ++i) { | ||
199 | workspace_name_from_binding(mode->keysym_bindings->items[i], | ||
200 | output_name, &order, &target); | ||
201 | } | ||
202 | for (int i = 0; i < mode->keycode_bindings->length; ++i) { | ||
203 | workspace_name_from_binding(mode->keycode_bindings->items[i], | ||
204 | output_name, &order, &target); | ||
200 | } | 205 | } |
201 | if (target != NULL) { | 206 | if (target != NULL) { |
202 | return target; | 207 | return target; |
@@ -529,3 +534,116 @@ void workspace_detect_urgent(struct sway_container *workspace) { | |||
529 | container_damage_whole(workspace); | 534 | container_damage_whole(workspace); |
530 | } | 535 | } |
531 | } | 536 | } |
537 | |||
538 | struct pid_workspace { | ||
539 | pid_t pid; | ||
540 | char *workspace; | ||
541 | struct timespec time_added; | ||
542 | |||
543 | struct sway_container *output; | ||
544 | struct wl_listener output_destroy; | ||
545 | |||
546 | struct wl_list link; | ||
547 | }; | ||
548 | |||
549 | static struct wl_list pid_workspaces; | ||
550 | |||
551 | struct sway_container *workspace_for_pid(pid_t pid) { | ||
552 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
553 | wl_list_init(&pid_workspaces); | ||
554 | return NULL; | ||
555 | } | ||
556 | |||
557 | struct sway_container *ws = NULL; | ||
558 | struct pid_workspace *pw = NULL; | ||
559 | |||
560 | wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); | ||
561 | |||
562 | do { | ||
563 | struct pid_workspace *_pw = NULL; | ||
564 | wl_list_for_each(_pw, &pid_workspaces, link) { | ||
565 | if (pid == _pw->pid) { | ||
566 | pw = _pw; | ||
567 | wlr_log(WLR_DEBUG, | ||
568 | "found pid_workspace for pid %d, workspace %s", | ||
569 | pid, pw->workspace); | ||
570 | goto found; | ||
571 | } | ||
572 | } | ||
573 | pid = get_parent_pid(pid); | ||
574 | } while (pid > 1); | ||
575 | found: | ||
576 | |||
577 | if (pw && pw->workspace) { | ||
578 | ws = workspace_by_name(pw->workspace); | ||
579 | |||
580 | if (!ws) { | ||
581 | wlr_log(WLR_DEBUG, | ||
582 | "Creating workspace %s for pid %d because it disappeared", | ||
583 | pw->workspace, pid); | ||
584 | ws = workspace_create(pw->output, pw->workspace); | ||
585 | } | ||
586 | |||
587 | wl_list_remove(&pw->output_destroy.link); | ||
588 | wl_list_remove(&pw->link); | ||
589 | free(pw->workspace); | ||
590 | free(pw); | ||
591 | } | ||
592 | |||
593 | return ws; | ||
594 | } | ||
595 | |||
596 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
597 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
598 | pw->output = NULL; | ||
599 | wl_list_remove(&pw->output_destroy.link); | ||
600 | wl_list_init(&pw->output_destroy.link); | ||
601 | } | ||
602 | |||
603 | void workspace_record_pid(pid_t pid) { | ||
604 | wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); | ||
605 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
606 | wl_list_init(&pid_workspaces); | ||
607 | } | ||
608 | |||
609 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
610 | struct sway_container *ws = | ||
611 | seat_get_focus_inactive(seat, &root_container); | ||
612 | if (ws && ws->type != C_WORKSPACE) { | ||
613 | ws = container_parent(ws, C_WORKSPACE); | ||
614 | } | ||
615 | if (!ws) { | ||
616 | wlr_log(WLR_DEBUG, "Bailing out, no workspace"); | ||
617 | return; | ||
618 | } | ||
619 | struct sway_container *output = ws->parent; | ||
620 | if (!output) { | ||
621 | wlr_log(WLR_DEBUG, "Bailing out, no output"); | ||
622 | return; | ||
623 | } | ||
624 | |||
625 | struct timespec now; | ||
626 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
627 | |||
628 | // Remove expired entries | ||
629 | static const int timeout = 60; | ||
630 | struct pid_workspace *old, *_old; | ||
631 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
632 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
633 | wl_list_remove(&old->output_destroy.link); | ||
634 | wl_list_remove(&old->link); | ||
635 | free(old->workspace); | ||
636 | free(old); | ||
637 | } | ||
638 | } | ||
639 | |||
640 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
641 | pw->workspace = strdup(ws->name); | ||
642 | pw->output = output; | ||
643 | pw->pid = pid; | ||
644 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
645 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
646 | wl_signal_add(&output->sway_output->wlr_output->events.destroy, | ||
647 | &pw->output_destroy); | ||
648 | wl_list_insert(&pid_workspaces, &pw->link); | ||
649 | } | ||