summaryrefslogtreecommitdiffstats
path: root/sway/tree
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/arrange.c18
-rw-r--r--sway/tree/container.c369
-rw-r--r--sway/tree/layout.c122
-rw-r--r--sway/tree/output.c2
-rw-r--r--sway/tree/view.c315
-rw-r--r--sway/tree/workspace.c268
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
227static void arrange_output(struct sway_container *output) { 241static 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
69static void container_update_textures_recursive(struct sway_container *con) { 72void 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) {
139static void container_workspace_free(struct sway_workspace *ws) { 148static 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
203static struct sway_container *container_destroy_noreaping(
204 struct sway_container *con);
205
196static struct sway_container *container_workspace_destroy( 206static 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 */
400struct sway_container *container_destroy(struct sway_container *con) { 424struct 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
510static struct sway_container *container_at_view(struct sway_container *swayc, 538struct 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
577static 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
621struct sway_container *container_at(struct sway_container *parent, 656static 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
653struct 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
687static 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
714static 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
742struct 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
683void container_for_each_descendant_dfs(struct sway_container *container, 771void 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
1025void 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
937void container_set_floating(struct sway_container *container, bool enable) { 1086void 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 */
1012static void container_floating_translate(struct sway_container *con, 1168void 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,
1105bool container_has_urgent_child(struct sway_container *container) { 1261bool 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
1265void 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
1274static 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
1284void 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
1338bool 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
1349bool 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
1360struct 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
63static void container_handle_fullscreen_reparent(struct sway_container *con, 67static 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
99void container_insert_child(struct sway_container *parent, 110void 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
140struct sway_container *container_remove_child(struct sway_container *child) { 155struct 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
567enum sway_container_layout container_get_default_layout( 608enum 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
111uint32_t view_get_x11_window_id(struct sway_view *view) { 116uint32_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
118const char *view_get_window_role(struct sway_view *view) { 123const 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
151void 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
144uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, 164uint32_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
152void 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
201void view_autoconfigure(struct sway_view *view) { 172void 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
333void 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
393void view_close(struct sway_view *view) { 300void 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
306void view_close_popups(struct sway_view *view) {
307 if (view->impl->close_popups) {
308 view->impl->close_popups(view);
309 }
310}
311
399void view_damage_from(struct sway_view *view) { 312void 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
342void 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
429static void view_subsurface_create(struct sway_view *view, 352static 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
447static 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
524static bool should_focus(struct sway_view *view) { 506static 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
920static bool find_by_mark_iterator(struct sway_container *con, 889static 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
946void view_clear_marks(struct sway_view *view) { 916void 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
954bool view_has_mark(struct sway_view *view, char *mark) { 923bool 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
933void 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
964static void update_marks_texture(struct sway_view *view, 938static 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) {
1117bool view_is_urgent(struct sway_view *view) { 1095bool 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
1099void 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
1107void 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
110char *workspace_next_name(const char *output_name) { 111static 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
189char *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
538struct 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
549static struct wl_list pid_workspaces;
550
551struct 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);
575found:
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
596static 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
603void 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}