aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-30 21:00:10 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-09-05 18:01:43 +1000
commit7586f150c058997d9dde387ea7c091ffa7a3c3c7 (patch)
tree63d19027974c1db62ce3a74ca1d2314eb6d5049b /sway/tree
parentMerge pull request #2569 from RyanDwyer/deny-reload-repeat (diff)
downloadsway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.gz
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.zst
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.zip
Implement type safe arguments and demote sway_container
This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/arrange.c110
-rw-r--r--sway/tree/container.c871
-rw-r--r--sway/tree/node.c151
-rw-r--r--sway/tree/output.c348
-rw-r--r--sway/tree/root.c186
-rw-r--r--sway/tree/view.c277
-rw-r--r--sway/tree/workspace.c493
7 files changed, 1164 insertions, 1272 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 92f20fcc..f86d4a74 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -166,29 +166,23 @@ void arrange_container(struct sway_container *container) {
166 if (config->reloading) { 166 if (config->reloading) {
167 return; 167 return;
168 } 168 }
169 if (container->type == C_VIEW) { 169 if (container->view) {
170 view_autoconfigure(container->sway_view); 170 view_autoconfigure(container->view);
171 container_set_dirty(container); 171 node_set_dirty(&container->node);
172 return;
173 }
174 if (!sway_assert(container->type == C_CONTAINER, "Expected a container")) {
175 return; 172 return;
176 } 173 }
177 struct wlr_box box; 174 struct wlr_box box;
178 container_get_box(container, &box); 175 container_get_box(container, &box);
179 arrange_children(container->children, container->layout, &box); 176 arrange_children(container->children, container->layout, &box);
180 container_set_dirty(container); 177 node_set_dirty(&container->node);
181} 178}
182 179
183void arrange_workspace(struct sway_container *workspace) { 180void arrange_workspace(struct sway_workspace *workspace) {
184 if (config->reloading) { 181 if (config->reloading) {
185 return; 182 return;
186 } 183 }
187 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 184 struct sway_output *output = workspace->output;
188 return; 185 struct wlr_box *area = &output->usable_area;
189 }
190 struct sway_container *output = workspace->parent;
191 struct wlr_box *area = &output->sway_output->usable_area;
192 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", 186 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
193 area->width, area->height, area->x, area->y); 187 area->width, area->height, area->x, area->y);
194 workspace_remove_gaps(workspace); 188 workspace_remove_gaps(workspace);
@@ -197,21 +191,20 @@ void arrange_workspace(struct sway_container *workspace) {
197 double prev_y = workspace->y; 191 double prev_y = workspace->y;
198 workspace->width = area->width; 192 workspace->width = area->width;
199 workspace->height = area->height; 193 workspace->height = area->height;
200 workspace->x = output->x + area->x; 194 workspace->x = output->wlr_output->lx + area->x;
201 workspace->y = output->y + area->y; 195 workspace->y = output->wlr_output->ly + area->y;
202 196
203 // Adjust any floating containers 197 // Adjust any floating containers
204 double diff_x = workspace->x - prev_x; 198 double diff_x = workspace->x - prev_x;
205 double diff_y = workspace->y - prev_y; 199 double diff_y = workspace->y - prev_y;
206 if (diff_x != 0 || diff_y != 0) { 200 if (diff_x != 0 || diff_y != 0) {
207 for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) { 201 for (int i = 0; i < workspace->floating->length; ++i) {
208 struct sway_container *floater = 202 struct sway_container *floater = workspace->floating->items[i];
209 workspace->sway_workspace->floating->items[i];
210 container_floating_translate(floater, diff_x, diff_y); 203 container_floating_translate(floater, diff_x, diff_y);
211 double center_x = floater->x + floater->width / 2; 204 double center_x = floater->x + floater->width / 2;
212 double center_y = floater->y + floater->height / 2; 205 double center_y = floater->y + floater->height / 2;
213 struct wlr_box workspace_box; 206 struct wlr_box workspace_box;
214 container_get_box(workspace, &workspace_box); 207 workspace_get_box(workspace, &workspace_box);
215 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 208 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) {
216 container_floating_move_to_center(floater); 209 container_floating_move_to_center(floater);
217 } 210 }
@@ -219,43 +212,32 @@ void arrange_workspace(struct sway_container *workspace) {
219 } 212 }
220 213
221 workspace_add_gaps(workspace); 214 workspace_add_gaps(workspace);
222 container_set_dirty(workspace); 215 node_set_dirty(&workspace->node);
223 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, 216 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
224 workspace->x, workspace->y); 217 workspace->x, workspace->y);
225 if (workspace->sway_workspace->fullscreen) { 218 if (workspace->fullscreen) {
226 struct sway_container *fs = workspace->sway_workspace->fullscreen; 219 struct sway_container *fs = workspace->fullscreen;
227 fs->x = workspace->parent->x; 220 fs->x = output->wlr_output->lx;
228 fs->y = workspace->parent->y; 221 fs->y = output->wlr_output->ly;
229 fs->width = workspace->parent->width; 222 fs->width = output->wlr_output->width;
230 fs->height = workspace->parent->height; 223 fs->height = output->wlr_output->height;
231 arrange_container(fs); 224 arrange_container(fs);
232 } else { 225 } else {
233 struct wlr_box box; 226 struct wlr_box box;
234 container_get_box(workspace, &box); 227 workspace_get_box(workspace, &box);
235 arrange_children(workspace->children, workspace->layout, &box); 228 arrange_children(workspace->tiling, workspace->layout, &box);
236 arrange_floating(workspace->sway_workspace->floating); 229 arrange_floating(workspace->floating);
237 } 230 }
238} 231}
239 232
240void arrange_output(struct sway_container *output) { 233void arrange_output(struct sway_output *output) {
241 if (config->reloading) { 234 if (config->reloading) {
242 return; 235 return;
243 } 236 }
244 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 237 // Outputs have no pending x/y/width/height,
245 return; 238 // so all we do here is arrange the workspaces.
246 } 239 for (int i = 0; i < output->workspaces->length; ++i) {
247 const struct wlr_box *output_box = wlr_output_layout_get_box( 240 struct sway_workspace *workspace = output->workspaces->items[i];
248 root_container.sway_root->output_layout,
249 output->sway_output->wlr_output);
250 output->x = output_box->x;
251 output->y = output_box->y;
252 output->width = output_box->width;
253 output->height = output_box->height;
254 container_set_dirty(output);
255 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
256 output->name, output->x, output->y);
257 for (int i = 0; i < output->children->length; ++i) {
258 struct sway_container *workspace = output->children->items[i];
259 arrange_workspace(workspace); 241 arrange_workspace(workspace);
260 } 242 }
261} 243}
@@ -264,37 +246,31 @@ void arrange_root(void) {
264 if (config->reloading) { 246 if (config->reloading) {
265 return; 247 return;
266 } 248 }
267 struct wlr_output_layout *output_layout =
268 root_container.sway_root->output_layout;
269 const struct wlr_box *layout_box = 249 const struct wlr_box *layout_box =
270 wlr_output_layout_get_box(output_layout, NULL); 250 wlr_output_layout_get_box(root->output_layout, NULL);
271 root_container.x = layout_box->x; 251 root->x = layout_box->x;
272 root_container.y = layout_box->y; 252 root->y = layout_box->y;
273 root_container.width = layout_box->width; 253 root->width = layout_box->width;
274 root_container.height = layout_box->height; 254 root->height = layout_box->height;
275 container_set_dirty(&root_container); 255 for (int i = 0; i < root->outputs->length; ++i) {
276 for (int i = 0; i < root_container.children->length; ++i) { 256 struct sway_output *output = root->outputs->items[i];
277 struct sway_container *output = root_container.children->items[i];
278 arrange_output(output); 257 arrange_output(output);
279 } 258 }
280} 259}
281 260
282void arrange_windows(struct sway_container *container) { 261void arrange_node(struct sway_node *node) {
283 switch (container->type) { 262 switch (node->type) {
284 case C_ROOT: 263 case N_ROOT:
285 arrange_root(); 264 arrange_root();
286 break; 265 break;
287 case C_OUTPUT: 266 case N_OUTPUT:
288 arrange_output(container); 267 arrange_output(node->sway_output);
289 break;
290 case C_WORKSPACE:
291 arrange_workspace(container);
292 break; 268 break;
293 case C_CONTAINER: 269 case N_WORKSPACE:
294 case C_VIEW: 270 arrange_workspace(node->sway_workspace);
295 arrange_container(container);
296 break; 271 break;
297 case C_TYPES: 272 case N_CONTAINER:
273 arrange_container(node->sway_container);
298 break; 274 break;
299 } 275 }
300} 276}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 520b4566..0cb8d0a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -24,97 +24,39 @@
24#include "log.h" 24#include "log.h"
25#include "stringop.h" 25#include "stringop.h"
26 26
27const char *container_type_to_str(enum sway_container_type type) { 27struct sway_container *container_create(struct sway_view *view) {
28 switch (type) {
29 case C_ROOT:
30 return "C_ROOT";
31 case C_OUTPUT:
32 return "C_OUTPUT";
33 case C_WORKSPACE:
34 return "C_WORKSPACE";
35 case C_CONTAINER:
36 return "C_CONTAINER";
37 case C_VIEW:
38 return "C_VIEW";
39 default:
40 return "C_UNKNOWN";
41 }
42}
43
44void container_create_notify(struct sway_container *container) {
45 if (container->type == C_VIEW) {
46 ipc_event_window(container, "new");
47 } else if (container->type == C_WORKSPACE) {
48 ipc_event_workspace(NULL, container, "init");
49 }
50 wl_signal_emit(&root_container.sway_root->events.new_container, container);
51}
52
53void container_update_textures_recursive(struct sway_container *con) {
54 if (con->type == C_CONTAINER || con->type == C_VIEW) {
55 container_update_title_textures(con);
56 }
57
58 if (con->type == C_VIEW) {
59 view_update_marks_textures(con->sway_view);
60 } else {
61 for (int i = 0; i < con->children->length; ++i) {
62 struct sway_container *child = con->children->items[i];
63 container_update_textures_recursive(child);
64 }
65
66 if (con->type == C_WORKSPACE) {
67 for (int i = 0; i < con->sway_workspace->floating->length; ++i) {
68 struct sway_container *floater =
69 con->sway_workspace->floating->items[i];
70 container_update_textures_recursive(floater);
71 }
72 }
73 }
74}
75
76struct sway_container *container_create(enum sway_container_type type) {
77 // next id starts at 1 because 0 is assigned to root_container in layout.c
78 static size_t next_id = 1;
79 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 28 struct sway_container *c = calloc(1, sizeof(struct sway_container));
80 if (!c) { 29 if (!c) {
30 wlr_log(WLR_ERROR, "Unable to allocate sway_container");
81 return NULL; 31 return NULL;
82 } 32 }
83 c->id = next_id++; 33 node_init(&c->node, N_CONTAINER, c);
84 c->layout = L_NONE; 34 c->layout = L_NONE;
85 c->type = type; 35 c->view = view;
86 c->alpha = 1.0f; 36 c->alpha = 1.0f;
87 37
88 if (type != C_VIEW) { 38 if (!view) {
89 c->children = create_list(); 39 c->children = create_list();
90 c->current.children = create_list(); 40 c->current.children = create_list();
91 } 41 }
92 c->outputs = create_list(); 42 c->outputs = create_list();
93 43
94 wl_signal_init(&c->events.destroy); 44 wl_signal_init(&c->events.destroy);
95 45 wl_signal_emit(&root->events.new_node, &c->node);
96 c->has_gaps = false;
97 c->gaps_inner = 0;
98 c->gaps_outer = 0;
99 c->current_gaps = 0;
100 46
101 return c; 47 return c;
102} 48}
103 49
104void container_destroy(struct sway_container *con) { 50void container_destroy(struct sway_container *con) {
105 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, 51 if (!sway_assert(con->node.destroying,
106 "Expected a container or view")) {
107 return;
108 }
109 if (!sway_assert(con->destroying,
110 "Tried to free container which wasn't marked as destroying")) { 52 "Tried to free container which wasn't marked as destroying")) {
111 return; 53 return;
112 } 54 }
113 if (!sway_assert(con->ntxnrefs == 0, "Tried to free container " 55 if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container "
114 "which is still referenced by transactions")) { 56 "which is still referenced by transactions")) {
115 return; 57 return;
116 } 58 }
117 free(con->name); 59 free(con->title);
118 free(con->formatted_title); 60 free(con->formatted_title);
119 wlr_texture_destroy(con->title_focused); 61 wlr_texture_destroy(con->title_focused);
120 wlr_texture_destroy(con->title_focused_inactive); 62 wlr_texture_destroy(con->title_focused_inactive);
@@ -124,14 +66,14 @@ void container_destroy(struct sway_container *con) {
124 list_free(con->current.children); 66 list_free(con->current.children);
125 list_free(con->outputs); 67 list_free(con->outputs);
126 68
127 if (con->type == C_VIEW) { 69 if (con->view) {
128 struct sway_view *view = con->sway_view; 70 struct sway_view *view = con->view;
129 view->swayc = NULL; 71 view->container = NULL;
130 free(view->title_format); 72 free(view->title_format);
131 view->title_format = NULL; 73 view->title_format = NULL;
132 74
133 if (view->destroying) { 75 if (view->destroying) {
134 view_destroy(view); 76 view_destroy(con->view);
135 } 77 }
136 } 78 }
137 79
@@ -139,115 +81,57 @@ void container_destroy(struct sway_container *con) {
139} 81}
140 82
141void container_begin_destroy(struct sway_container *con) { 83void container_begin_destroy(struct sway_container *con) {
142 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, 84 if (con->view) {
143 "Expected a container or view")) {
144 return;
145 }
146
147 if (con->type == C_VIEW) {
148 ipc_event_window(con, "close"); 85 ipc_event_window(con, "close");
149 } 86 }
150 wl_signal_emit(&con->events.destroy, con); 87 wl_signal_emit(&con->node.events.destroy, &con->node);
151 88
152 container_end_mouse_operation(con); 89 container_end_mouse_operation(con);
153 90
154 con->destroying = true; 91 con->node.destroying = true;
155 container_set_dirty(con); 92 node_set_dirty(&con->node);
156 93
157 if (con->scratchpad) { 94 if (con->scratchpad) {
158 root_scratchpad_remove_container(con); 95 root_scratchpad_remove_container(con);
159 } 96 }
160 97
161 if (con->parent) { 98 if (con->parent || con->workspace) {
162 container_remove_child(con); 99 container_detach(con);
163 } 100 }
164} 101}
165 102
166struct sway_container *container_reap_empty(struct sway_container *con) { 103void container_reap_empty(struct sway_container *con) {
167 while (con && con->type == C_CONTAINER) { 104 if (con->view) {
168 struct sway_container *next = con->parent; 105 return;
169 if (con->children->length == 0) {
170 container_begin_destroy(con);
171 }
172 con = next;
173 } 106 }
174 if (con && con->type == C_WORKSPACE) { 107 struct sway_workspace *ws = con->workspace;
175 workspace_consider_destroy(con); 108 while (con) {
176 if (con->destroying) { 109 if (con->children->length) {
177 con = con->parent; 110 return;
178 } 111 }
112 struct sway_container *parent = con->parent;
113 container_begin_destroy(con);
114 con = parent;
179 } 115 }
180 return con; 116 workspace_consider_destroy(ws);
181} 117}
182 118
183struct sway_container *container_flatten(struct sway_container *container) { 119struct sway_container *container_flatten(struct sway_container *container) {
184 while (container->type == C_CONTAINER && container->children->length == 1) { 120 if (container->view) {
121 return NULL;
122 }
123 while (container && container->children->length == 1) {
185 struct sway_container *child = container->children->items[0]; 124 struct sway_container *child = container->children->items[0];
186 struct sway_container *parent = container->parent; 125 struct sway_container *parent = container->parent;
187 container_replace_child(container, child); 126 container_replace(container, child);
188 container_begin_destroy(container); 127 container_begin_destroy(container);
189 container = parent; 128 container = parent;
190 } 129 }
191 return container; 130 return container;
192} 131}
193 132
194static void container_close_func(struct sway_container *container, void *data) {
195 if (container->type == C_VIEW) {
196 view_close(container->sway_view);
197 }
198}
199
200struct sway_container *container_close(struct sway_container *con) {
201 if (!sway_assert(con != NULL,
202 "container_close called with a NULL container")) {
203 return NULL;
204 }
205
206 struct sway_container *parent = con->parent;
207
208 if (con->type == C_VIEW) {
209 view_close(con->sway_view);
210 } else if (con->type == C_CONTAINER) {
211 container_for_each_child(con, container_close_func, NULL);
212 } else if (con->type == C_WORKSPACE) {
213 workspace_for_each_container(con, container_close_func, NULL);
214 }
215
216 return parent;
217}
218
219struct sway_container *container_view_create(struct sway_container *sibling,
220 struct sway_view *sway_view) {
221 if (!sway_assert(sibling,
222 "container_view_create called with NULL sibling/parent")) {
223 return NULL;
224 }
225 const char *title = view_get_title(sway_view);
226 struct sway_container *swayc = container_create(C_VIEW);
227 wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s",
228 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
229 // Setup values
230 swayc->sway_view = sway_view;
231 swayc->width = 0;
232 swayc->height = 0;
233
234 if (sibling->type == C_WORKSPACE) {
235 // Case of focused workspace, just create as child of it
236 container_add_child(sibling, swayc);
237 } else {
238 // Regular case, create as sibling of current container
239 container_add_sibling(sibling, swayc);
240 }
241 container_create_notify(swayc);
242 return swayc;
243}
244
245struct sway_container *container_find_child(struct sway_container *container, 133struct sway_container *container_find_child(struct sway_container *container,
246 bool (*test)(struct sway_container *view, void *data), void *data) { 134 bool (*test)(struct sway_container *con, void *data), void *data) {
247 if (!sway_assert(container->type == C_CONTAINER ||
248 container->type == C_VIEW, "Expected a container or view")) {
249 return NULL;
250 }
251 if (!container->children) { 135 if (!container->children) {
252 return NULL; 136 return NULL;
253 } 137 }
@@ -264,46 +148,32 @@ struct sway_container *container_find_child(struct sway_container *container,
264 return NULL; 148 return NULL;
265} 149}
266 150
267struct sway_container *container_parent(struct sway_container *container, 151static void surface_at_view(struct sway_container *con, double lx, double ly,
268 enum sway_container_type type) {
269 if (!sway_assert(container, "container is NULL")) {
270 return NULL;
271 }
272 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
273 return NULL;
274 }
275 do {
276 container = container->parent;
277 } while (container && container->type != type);
278 return container;
279}
280
281static void surface_at_view(struct sway_container *swayc, double lx, double ly,
282 struct wlr_surface **surface, double *sx, double *sy) { 152 struct wlr_surface **surface, double *sx, double *sy) {
283 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { 153 if (!sway_assert(con->view, "Expected a view")) {
284 return; 154 return;
285 } 155 }
286 struct sway_view *sview = swayc->sway_view; 156 struct sway_view *view = con->view;
287 double view_sx = lx - sview->x + sview->geometry.x; 157 double view_sx = lx - view->x + view->geometry.x;
288 double view_sy = ly - sview->y + sview->geometry.y; 158 double view_sy = ly - view->y + view->geometry.y;
289 159
290 double _sx, _sy; 160 double _sx, _sy;
291 struct wlr_surface *_surface = NULL; 161 struct wlr_surface *_surface = NULL;
292 switch (sview->type) { 162 switch (view->type) {
293#ifdef HAVE_XWAYLAND 163#ifdef HAVE_XWAYLAND
294 case SWAY_VIEW_XWAYLAND: 164 case SWAY_VIEW_XWAYLAND:
295 _surface = wlr_surface_surface_at(sview->surface, 165 _surface = wlr_surface_surface_at(view->surface,
296 view_sx, view_sy, &_sx, &_sy); 166 view_sx, view_sy, &_sx, &_sy);
297 break; 167 break;
298#endif 168#endif
299 case SWAY_VIEW_XDG_SHELL_V6: 169 case SWAY_VIEW_XDG_SHELL_V6:
300 _surface = wlr_xdg_surface_v6_surface_at( 170 _surface = wlr_xdg_surface_v6_surface_at(
301 sview->wlr_xdg_surface_v6, 171 view->wlr_xdg_surface_v6,
302 view_sx, view_sy, &_sx, &_sy); 172 view_sx, view_sy, &_sx, &_sy);
303 break; 173 break;
304 case SWAY_VIEW_XDG_SHELL: 174 case SWAY_VIEW_XDG_SHELL:
305 _surface = wlr_xdg_surface_surface_at( 175 _surface = wlr_xdg_surface_surface_at(
306 sview->wlr_xdg_surface, 176 view->wlr_xdg_surface,
307 view_sx, view_sy, &_sx, &_sy); 177 view_sx, view_sy, &_sx, &_sy);
308 break; 178 break;
309 } 179 }
@@ -317,65 +187,72 @@ static void surface_at_view(struct sway_container *swayc, double lx, double ly,
317/** 187/**
318 * container_at for a container with layout L_TABBED. 188 * container_at for a container with layout L_TABBED.
319 */ 189 */
320static struct sway_container *container_at_tabbed(struct sway_container *parent, 190static struct sway_container *container_at_tabbed(struct sway_node *parent,
321 double lx, double ly, 191 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) { 192 struct wlr_surface **surface, double *sx, double *sy) {
323 if (ly < parent->y || ly > parent->y + parent->height) { 193 struct wlr_box box;
194 node_get_box(parent, &box);
195 if (ly < box.y || ly > box.y + box.height) {
324 return NULL; 196 return NULL;
325 } 197 }
326 struct sway_seat *seat = input_manager_current_seat(input_manager); 198 struct sway_seat *seat = input_manager_current_seat(input_manager);
199 list_t *children = node_get_children(parent);
327 200
328 // Tab titles 201 // Tab titles
329 int title_height = container_titlebar_height(); 202 int title_height = container_titlebar_height();
330 if (ly < parent->y + title_height) { 203 if (ly < box.y + title_height) {
331 int tab_width = parent->width / parent->children->length; 204 int tab_width = box.width / children->length;
332 int child_index = (lx - parent->x) / tab_width; 205 int child_index = (lx - box.x) / tab_width;
333 if (child_index >= parent->children->length) { 206 if (child_index >= children->length) {
334 child_index = parent->children->length - 1; 207 child_index = children->length - 1;
335 } 208 }
336 struct sway_container *child = parent->children->items[child_index]; 209 struct sway_container *child = children->items[child_index];
337 return seat_get_focus_inactive(seat, child); 210 struct sway_node *node = seat_get_focus_inactive(seat, &child->node);
211 return node->sway_container;
338 } 212 }
339 213
340 // Surfaces 214 // Surfaces
341 struct sway_container *current = seat_get_active_child(seat, parent); 215 struct sway_node *current = seat_get_active_child(seat, parent);
342
343 return tiling_container_at(current, lx, ly, surface, sx, sy); 216 return tiling_container_at(current, lx, ly, surface, sx, sy);
344} 217}
345 218
346/** 219/**
347 * container_at for a container with layout L_STACKED. 220 * container_at for a container with layout L_STACKED.
348 */ 221 */
349static struct sway_container *container_at_stacked( 222static struct sway_container *container_at_stacked(struct sway_node *parent,
350 struct sway_container *parent, double lx, double ly, 223 double lx, double ly,
351 struct wlr_surface **surface, double *sx, double *sy) { 224 struct wlr_surface **surface, double *sx, double *sy) {
352 if (ly < parent->y || ly > parent->y + parent->height) { 225 struct wlr_box box;
226 node_get_box(parent, &box);
227 if (ly < box.y || ly > box.y + box.height) {
353 return NULL; 228 return NULL;
354 } 229 }
355 struct sway_seat *seat = input_manager_current_seat(input_manager); 230 struct sway_seat *seat = input_manager_current_seat(input_manager);
231 list_t *children = node_get_children(parent);
356 232
357 // Title bars 233 // Title bars
358 int title_height = container_titlebar_height(); 234 int title_height = container_titlebar_height();
359 int child_index = (ly - parent->y) / title_height; 235 int child_index = (ly - box.y) / title_height;
360 if (child_index < parent->children->length) { 236 if (child_index < children->length) {
361 struct sway_container *child = parent->children->items[child_index]; 237 struct sway_container *child = children->items[child_index];
362 return seat_get_focus_inactive(seat, child); 238 struct sway_node *node = seat_get_focus_inactive(seat, &child->node);
239 return node->sway_container;
363 } 240 }
364 241
365 // Surfaces 242 // Surfaces
366 struct sway_container *current = seat_get_active_child(seat, parent); 243 struct sway_node *current = seat_get_active_child(seat, parent);
367
368 return tiling_container_at(current, lx, ly, surface, sx, sy); 244 return tiling_container_at(current, lx, ly, surface, sx, sy);
369} 245}
370 246
371/** 247/**
372 * container_at for a container with layout L_HORIZ or L_VERT. 248 * container_at for a container with layout L_HORIZ or L_VERT.
373 */ 249 */
374static struct sway_container *container_at_linear(struct sway_container *parent, 250static struct sway_container *container_at_linear(struct sway_node *parent,
375 double lx, double ly, 251 double lx, double ly,
376 struct wlr_surface **surface, double *sx, double *sy) { 252 struct wlr_surface **surface, double *sx, double *sy) {
377 for (int i = 0; i < parent->children->length; ++i) { 253 list_t *children = node_get_children(parent);
378 struct sway_container *child = parent->children->items[i]; 254 for (int i = 0; i < children->length; ++i) {
255 struct sway_container *child = children->items[i];
379 struct wlr_box box = { 256 struct wlr_box box = {
380 .x = child->x, 257 .x = child->x,
381 .y = child->y, 258 .y = child->y,
@@ -383,7 +260,7 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
383 .height = child->height, 260 .height = child->height,
384 }; 261 };
385 if (wlr_box_contains_point(&box, lx, ly)) { 262 if (wlr_box_contains_point(&box, lx, ly)) {
386 return tiling_container_at(child, lx, ly, surface, sx, sy); 263 return tiling_container_at(&child->node, lx, ly, surface, sx, sy);
387 } 264 }
388 } 265 }
389 return NULL; 266 return NULL;
@@ -391,12 +268,11 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
391 268
392static struct sway_container *floating_container_at(double lx, double ly, 269static struct sway_container *floating_container_at(double lx, double ly,
393 struct wlr_surface **surface, double *sx, double *sy) { 270 struct wlr_surface **surface, double *sx, double *sy) {
394 for (int i = 0; i < root_container.children->length; ++i) { 271 for (int i = 0; i < root->outputs->length; ++i) {
395 struct sway_container *output = root_container.children->items[i]; 272 struct sway_output *output = root->outputs->items[i];
396 for (int j = 0; j < output->children->length; ++j) { 273 for (int j = 0; j < output->workspaces->length; ++j) {
397 struct sway_container *workspace = output->children->items[j]; 274 struct sway_workspace *ws = output->workspaces->items[j];
398 struct sway_workspace *ws = workspace->sway_workspace; 275 if (!workspace_is_visible(ws)) {
399 if (!workspace_is_visible(workspace)) {
400 continue; 276 continue;
401 } 277 }
402 // Items at the end of the list are on top, so iterate the list in 278 // Items at the end of the list are on top, so iterate the list in
@@ -410,7 +286,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
410 .height = floater->height, 286 .height = floater->height,
411 }; 287 };
412 if (wlr_box_contains_point(&box, lx, ly)) { 288 if (wlr_box_contains_point(&box, lx, ly)) {
413 return tiling_container_at(floater, lx, ly, 289 return tiling_container_at(&floater->node, lx, ly,
414 surface, sx, sy); 290 surface, sx, sy);
415 } 291 }
416 } 292 }
@@ -419,25 +295,24 @@ static struct sway_container *floating_container_at(double lx, double ly,
419 return NULL; 295 return NULL;
420} 296}
421 297
422struct sway_container *tiling_container_at( 298struct sway_container *tiling_container_at(struct sway_node *parent,
423 struct sway_container *con, double lx, double ly, 299 double lx, double ly,
424 struct wlr_surface **surface, double *sx, double *sy) { 300 struct wlr_surface **surface, double *sx, double *sy) {
425 if (con->type == C_VIEW) { 301 if (node_is_view(parent)) {
426 surface_at_view(con, lx, ly, surface, sx, sy); 302 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
427 return con; 303 return parent->sway_container;
428 } 304 }
429 if (!con->children->length) { 305 if (!node_get_children(parent)) {
430 return NULL; 306 return NULL;
431 } 307 }
432 308 switch (node_get_layout(parent)) {
433 switch (con->layout) {
434 case L_HORIZ: 309 case L_HORIZ:
435 case L_VERT: 310 case L_VERT:
436 return container_at_linear(con, lx, ly, surface, sx, sy); 311 return container_at_linear(parent, lx, ly, surface, sx, sy);
437 case L_TABBED: 312 case L_TABBED:
438 return container_at_tabbed(con, lx, ly, surface, sx, sy); 313 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
439 case L_STACKED: 314 case L_STACKED:
440 return container_at_stacked(con, lx, ly, surface, sx, sy); 315 return container_at_stacked(parent, lx, ly, surface, sx, sy);
441 case L_NONE: 316 case L_NONE:
442 return NULL; 317 return NULL;
443 } 318 }
@@ -472,19 +347,16 @@ static bool surface_is_popup(struct wlr_surface *surface) {
472 return false; 347 return false;
473} 348}
474 349
475struct sway_container *container_at(struct sway_container *workspace, 350struct sway_container *container_at(struct sway_workspace *workspace,
476 double lx, double ly, 351 double lx, double ly,
477 struct wlr_surface **surface, double *sx, double *sy) { 352 struct wlr_surface **surface, double *sx, double *sy) {
478 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
479 return NULL;
480 }
481 struct sway_container *c; 353 struct sway_container *c;
354 // Focused view's popups
482 struct sway_seat *seat = input_manager_current_seat(input_manager); 355 struct sway_seat *seat = input_manager_current_seat(input_manager);
483 struct sway_container *focus = 356 struct sway_container *focus = seat_get_focused_container(seat);
484 seat_get_focus_inactive(seat, &root_container);
485 bool is_floating = focus && container_is_floating_or_child(focus); 357 bool is_floating = focus && container_is_floating_or_child(focus);
486 // Focused view's popups 358 // Focused view's popups
487 if (focus && focus->type == C_VIEW) { 359 if (focus && focus->view) {
488 surface_at_view(focus, lx, ly, surface, sx, sy); 360 surface_at_view(focus, lx, ly, surface, sx, sy);
489 if (*surface && surface_is_popup(*surface)) { 361 if (*surface && surface_is_popup(*surface)) {
490 return focus; 362 return focus;
@@ -492,7 +364,7 @@ struct sway_container *container_at(struct sway_container *workspace,
492 *surface = NULL; 364 *surface = NULL;
493 } 365 }
494 // If focused is floating, focused view's non-popups 366 // If focused is floating, focused view's non-popups
495 if (focus && focus->type == C_VIEW && is_floating) { 367 if (focus && focus->view && is_floating) {
496 surface_at_view(focus, lx, ly, surface, sx, sy); 368 surface_at_view(focus, lx, ly, surface, sx, sy);
497 if (*surface) { 369 if (*surface) {
498 return focus; 370 return focus;
@@ -504,7 +376,7 @@ struct sway_container *container_at(struct sway_container *workspace,
504 return c; 376 return c;
505 } 377 }
506 // If focused is tiling, focused view's non-popups 378 // If focused is tiling, focused view's non-popups
507 if (focus && focus->type == C_VIEW && !is_floating) { 379 if (focus && focus->view && !is_floating) {
508 surface_at_view(focus, lx, ly, surface, sx, sy); 380 surface_at_view(focus, lx, ly, surface, sx, sy);
509 if (*surface) { 381 if (*surface) {
510 return focus; 382 return focus;
@@ -512,7 +384,7 @@ struct sway_container *container_at(struct sway_container *workspace,
512 *surface = NULL; 384 *surface = NULL;
513 } 385 }
514 // Tiling (non-focused) 386 // Tiling (non-focused)
515 if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { 387 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
516 return c; 388 return c;
517 } 389 }
518 return NULL; 390 return NULL;
@@ -521,10 +393,6 @@ struct sway_container *container_at(struct sway_container *workspace,
521void container_for_each_child(struct sway_container *container, 393void container_for_each_child(struct sway_container *container,
522 void (*f)(struct sway_container *container, void *data), 394 void (*f)(struct sway_container *container, void *data),
523 void *data) { 395 void *data) {
524 if (!sway_assert(container->type == C_CONTAINER ||
525 container->type == C_VIEW, "Expected a container or view")) {
526 return;
527 }
528 if (container->children) { 396 if (container->children) {
529 for (int i = 0; i < container->children->length; ++i) { 397 for (int i = 0; i < container->children->length; ++i) {
530 struct sway_container *child = container->children->items[i]; 398 struct sway_container *child = container->children->items[i];
@@ -536,7 +404,7 @@ void container_for_each_child(struct sway_container *container,
536 404
537bool container_has_ancestor(struct sway_container *descendant, 405bool container_has_ancestor(struct sway_container *descendant,
538 struct sway_container *ancestor) { 406 struct sway_container *ancestor) {
539 while (descendant && descendant->type != C_ROOT) { 407 while (descendant) {
540 descendant = descendant->parent; 408 descendant = descendant->parent;
541 if (descendant == ancestor) { 409 if (descendant == ancestor) {
542 return true; 410 return true;
@@ -545,27 +413,10 @@ bool container_has_ancestor(struct sway_container *descendant,
545 return false; 413 return false;
546} 414}
547 415
548int container_count_descendants_of_type(struct sway_container *con,
549 enum sway_container_type type) {
550 int children = 0;
551 if (con->type == type) {
552 children++;
553 }
554 if (con->children) {
555 for (int i = 0; i < con->children->length; i++) {
556 struct sway_container *child = con->children->items[i];
557 children += container_count_descendants_of_type(child, type);
558 }
559 }
560 return children;
561}
562
563void container_damage_whole(struct sway_container *container) { 416void container_damage_whole(struct sway_container *container) {
564 for (int i = 0; i < root_container.children->length; ++i) { 417 for (int i = 0; i < root->outputs->length; ++i) {
565 struct sway_container *cont = root_container.children->items[i]; 418 struct sway_output *output = root->outputs->items[i];
566 if (cont->type == C_OUTPUT) { 419 output_damage_whole_container(output, container);
567 output_damage_whole_container(cont->sway_output, container);
568 }
569 } 420 }
570} 421}
571 422
@@ -582,10 +433,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con) {
582 433
583static void update_title_texture(struct sway_container *con, 434static void update_title_texture(struct sway_container *con,
584 struct wlr_texture **texture, struct border_colors *class) { 435 struct wlr_texture **texture, struct border_colors *class) {
585 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
586 "Unexpected type %s", container_type_to_str(con->type))) {
587 return;
588 }
589 struct sway_output *output = container_get_effective_output(con); 436 struct sway_output *output = container_get_effective_output(con);
590 if (!output) { 437 if (!output) {
591 return; 438 return;
@@ -664,9 +511,10 @@ void container_calculate_title_height(struct sway_container *container) {
664 * An example tree representation is: V[Terminal, Firefox] 511 * An example tree representation is: V[Terminal, Firefox]
665 * If buffer is not NULL, also populate the buffer with the representation. 512 * If buffer is not NULL, also populate the buffer with the representation.
666 */ 513 */
667static size_t get_tree_representation(struct sway_container *parent, char *buffer) { 514size_t container_build_representation(enum sway_container_layout layout,
515 list_t *children, char *buffer) {
668 size_t len = 2; 516 size_t len = 2;
669 switch (parent->layout) { 517 switch (layout) {
670 case L_VERT: 518 case L_VERT:
671 lenient_strcat(buffer, "V["); 519 lenient_strcat(buffer, "V[");
672 break; 520 break;
@@ -683,17 +531,17 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe
683 lenient_strcat(buffer, "D["); 531 lenient_strcat(buffer, "D[");
684 break; 532 break;
685 } 533 }
686 for (int i = 0; i < parent->children->length; ++i) { 534 for (int i = 0; i < children->length; ++i) {
687 if (i != 0) { 535 if (i != 0) {
688 ++len; 536 ++len;
689 lenient_strcat(buffer, " "); 537 lenient_strcat(buffer, " ");
690 } 538 }
691 struct sway_container *child = parent->children->items[i]; 539 struct sway_container *child = children->items[i];
692 const char *identifier = NULL; 540 const char *identifier = NULL;
693 if (child->type == C_VIEW) { 541 if (child->view) {
694 identifier = view_get_class(child->sway_view); 542 identifier = view_get_class(child->view);
695 if (!identifier) { 543 if (!identifier) {
696 identifier = view_get_app_id(child->sway_view); 544 identifier = view_get_app_id(child->view);
697 } 545 }
698 } else { 546 } else {
699 identifier = child->formatted_title; 547 identifier = child->formatted_title;
@@ -711,25 +559,25 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe
711 return len; 559 return len;
712} 560}
713 561
714void container_notify_subtree_changed(struct sway_container *container) { 562void container_update_representation(struct sway_container *con) {
715 if (!container || container->type < C_WORKSPACE) { 563 if (!con->view) {
716 return; 564 size_t len = container_build_representation(con->layout,
717 } 565 con->children, NULL);
718 free(container->formatted_title); 566 free(con->formatted_title);
719 container->formatted_title = NULL; 567 con->formatted_title = calloc(len + 1, sizeof(char));
720 568 if (!sway_assert(con->formatted_title,
721 size_t len = get_tree_representation(container, NULL); 569 "Unable to allocate title string")) {
722 char *buffer = calloc(len + 1, sizeof(char)); 570 return;
723 if (!sway_assert(buffer, "Unable to allocate title string")) { 571 }
724 return; 572 container_build_representation(con->layout, con->children,
573 con->formatted_title);
574 container_calculate_title_height(con);
575 container_update_title_textures(con);
725 } 576 }
726 get_tree_representation(container, buffer); 577 if (con->parent) {
727 578 container_update_representation(con->parent);
728 container->formatted_title = buffer; 579 } else if (con->workspace) {
729 if (container->type != C_WORKSPACE) { 580 workspace_update_representation(con->workspace);
730 container_calculate_title_height(container);
731 container_update_title_textures(container);
732 container_notify_subtree_changed(container->parent);
733 } 581 }
734} 582}
735 583
@@ -738,11 +586,7 @@ size_t container_titlebar_height() {
738} 586}
739 587
740void container_init_floating(struct sway_container *con) { 588void container_init_floating(struct sway_container *con) {
741 if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, 589 struct sway_workspace *ws = con->workspace;
742 "Expected a view or container")) {
743 return;
744 }
745 struct sway_container *ws = container_parent(con, C_WORKSPACE);
746 int min_width, min_height; 590 int min_width, min_height;
747 int max_width, max_height; 591 int max_width, max_height;
748 592
@@ -778,13 +622,13 @@ void container_init_floating(struct sway_container *con) {
778 max_height = config->floating_maximum_height; 622 max_height = config->floating_maximum_height;
779 } 623 }
780 624
781 if (con->type == C_CONTAINER) { 625 if (!con->view) {
782 con->width = max_width; 626 con->width = max_width;
783 con->height = max_height; 627 con->height = max_height;
784 con->x = ws->x + (ws->width - con->width) / 2; 628 con->x = ws->x + (ws->width - con->width) / 2;
785 con->y = ws->y + (ws->height - con->height) / 2; 629 con->y = ws->y + (ws->height - con->height) / 2;
786 } else { 630 } else {
787 struct sway_view *view = con->sway_view; 631 struct sway_view *view = con->view;
788 view->width = fmax(min_width, fmin(view->natural_width, max_width)); 632 view->width = fmax(min_width, fmin(view->natural_width, max_width));
789 view->height = fmax(min_height, fmin(view->natural_height, max_height)); 633 view->height = fmax(min_height, fmin(view->natural_height, max_height));
790 view->x = ws->x + (ws->width - view->width) / 2; 634 view->x = ws->x + (ws->width - view->width) / 2;
@@ -794,7 +638,7 @@ void container_init_floating(struct sway_container *con) {
794 view->border_top = view->border_bottom = true; 638 view->border_top = view->border_bottom = true;
795 view->border_left = view->border_right = true; 639 view->border_left = view->border_right = true;
796 640
797 container_set_geometry_from_floating_view(view->swayc); 641 container_set_geometry_from_floating_view(con);
798 } 642 }
799} 643}
800 644
@@ -804,32 +648,41 @@ void container_set_floating(struct sway_container *container, bool enable) {
804 } 648 }
805 649
806 struct sway_seat *seat = input_manager_current_seat(input_manager); 650 struct sway_seat *seat = input_manager_current_seat(input_manager);
807 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 651 struct sway_workspace *workspace = container->workspace;
808 652
809 if (enable) { 653 if (enable) {
810 struct sway_container *old_parent = container_remove_child(container); 654 struct sway_container *old_parent = container->parent;
655 container_detach(container);
811 workspace_add_floating(workspace, container); 656 workspace_add_floating(workspace, container);
812 container_init_floating(container); 657 container_init_floating(container);
813 if (container->type == C_VIEW) { 658 if (container->view) {
814 view_set_tiled(container->sway_view, false); 659 view_set_tiled(container->view, false);
660 }
661 if (old_parent) {
662 container_reap_empty(old_parent);
815 } 663 }
816 container_reap_empty(old_parent);
817 } else { 664 } else {
818 // Returning to tiled 665 // Returning to tiled
819 if (container->scratchpad) { 666 if (container->scratchpad) {
820 root_scratchpad_remove_container(container); 667 root_scratchpad_remove_container(container);
821 } 668 }
822 container_remove_child(container); 669 container_detach(container);
823 struct sway_container *reference = 670 struct sway_container *reference =
824 seat_get_focus_inactive_tiling(seat, workspace); 671 seat_get_focus_inactive_tiling(seat, workspace);
825 if (reference->type == C_VIEW) { 672 if (reference && reference->view) {
826 reference = reference->parent; 673 reference = reference->parent;
827 } 674 }
828 container_add_child(reference, container); 675 if (reference) {
829 container->width = container->parent->width; 676 container_add_child(reference, container);
830 container->height = container->parent->height; 677 container->width = reference->width;
831 if (container->type == C_VIEW) { 678 container->height = reference->height;
832 view_set_tiled(container->sway_view, true); 679 } else {
680 workspace_add_tiling(workspace, container);
681 container->width = workspace->width;
682 container->height = workspace->height;
683 }
684 if (container->view) {
685 view_set_tiled(container->view, true);
833 } 686 }
834 container->is_sticky = false; 687 container->is_sticky = false;
835 } 688 }
@@ -840,14 +693,13 @@ void container_set_floating(struct sway_container *container, bool enable) {
840} 693}
841 694
842void container_set_geometry_from_floating_view(struct sway_container *con) { 695void container_set_geometry_from_floating_view(struct sway_container *con) {
843 if (!sway_assert(con->type == C_VIEW, "Expected a view")) { 696 if (!sway_assert(con->view, "Expected a view")) {
844 return; 697 return;
845 } 698 }
846 if (!sway_assert(container_is_floating(con), 699 if (!sway_assert(container_is_floating(con), "Expected a floating view")) {
847 "Expected a floating view")) {
848 return; 700 return;
849 } 701 }
850 struct sway_view *view = con->sway_view; 702 struct sway_view *view = con->view;
851 size_t border_width = 0; 703 size_t border_width = 0;
852 size_t top = 0; 704 size_t top = 0;
853 705
@@ -861,12 +713,12 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
861 con->y = view->y - top; 713 con->y = view->y - top;
862 con->width = view->width + border_width * 2; 714 con->width = view->width + border_width * 2;
863 con->height = top + view->height + border_width; 715 con->height = top + view->height + border_width;
864 container_set_dirty(con); 716 node_set_dirty(&con->node);
865} 717}
866 718
867bool container_is_floating(struct sway_container *container) { 719bool container_is_floating(struct sway_container *container) {
868 return container->parent && container->parent->type == C_WORKSPACE && 720 return !container->parent && container->workspace &&
869 list_find(container->parent->sway_workspace->floating, container) != -1; 721 list_find(container->workspace->floating, container) != -1;
870} 722}
871 723
872void container_get_box(struct sway_container *container, struct wlr_box *box) { 724void container_get_box(struct sway_container *container, struct wlr_box *box) {
@@ -883,16 +735,16 @@ void container_floating_translate(struct sway_container *con,
883 double x_amount, double y_amount) { 735 double x_amount, double y_amount) {
884 con->x += x_amount; 736 con->x += x_amount;
885 con->y += y_amount; 737 con->y += y_amount;
886 if (con->type == C_VIEW) { 738 if (con->view) {
887 con->sway_view->x += x_amount; 739 con->view->x += x_amount;
888 con->sway_view->y += y_amount; 740 con->view->y += y_amount;
889 } else { 741 } else {
890 for (int i = 0; i < con->children->length; ++i) { 742 for (int i = 0; i < con->children->length; ++i) {
891 struct sway_container *child = con->children->items[i]; 743 struct sway_container *child = con->children->items[i];
892 container_floating_translate(child, x_amount, y_amount); 744 container_floating_translate(child, x_amount, y_amount);
893 } 745 }
894 } 746 }
895 container_set_dirty(con); 747 node_set_dirty(&con->node);
896} 748}
897 749
898/** 750/**
@@ -902,17 +754,16 @@ void container_floating_translate(struct sway_container *con,
902 * one, otherwise we'll choose whichever output is closest to the container's 754 * one, otherwise we'll choose whichever output is closest to the container's
903 * center. 755 * center.
904 */ 756 */
905struct sway_container *container_floating_find_output( 757struct sway_output *container_floating_find_output(struct sway_container *con) {
906 struct sway_container *con) {
907 double center_x = con->x + con->width / 2; 758 double center_x = con->x + con->width / 2;
908 double center_y = con->y + con->height / 2; 759 double center_y = con->y + con->height / 2;
909 struct sway_container *closest_output = NULL; 760 struct sway_output *closest_output = NULL;
910 double closest_distance = DBL_MAX; 761 double closest_distance = DBL_MAX;
911 for (int i = 0; i < root_container.children->length; ++i) { 762 for (int i = 0; i < root->outputs->length; ++i) {
912 struct sway_container *output = root_container.children->items[i]; 763 struct sway_output *output = root->outputs->items[i];
913 struct wlr_box output_box; 764 struct wlr_box output_box;
914 double closest_x, closest_y; 765 double closest_x, closest_y;
915 container_get_box(output, &output_box); 766 output_get_box(output, &output_box);
916 wlr_box_closest_point(&output_box, center_x, center_y, 767 wlr_box_closest_point(&output_box, center_x, center_y,
917 &closest_x, &closest_y); 768 &closest_x, &closest_y);
918 if (center_x == closest_x && center_y == closest_y) { 769 if (center_x == closest_x && center_y == closest_y) {
@@ -937,18 +788,18 @@ void container_floating_move_to(struct sway_container *con,
937 return; 788 return;
938 } 789 }
939 container_floating_translate(con, lx - con->x, ly - con->y); 790 container_floating_translate(con, lx - con->x, ly - con->y);
940 struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); 791 struct sway_workspace *old_workspace = con->workspace;
941 struct sway_container *new_output = container_floating_find_output(con); 792 struct sway_output *new_output = container_floating_find_output(con);
942 if (!sway_assert(new_output, "Unable to find any output")) { 793 if (!sway_assert(new_output, "Unable to find any output")) {
943 return; 794 return;
944 } 795 }
945 struct sway_container *new_workspace = 796 struct sway_workspace *new_workspace =
946 output_get_active_workspace(new_output->sway_output); 797 output_get_active_workspace(new_output);
947 if (old_workspace != new_workspace) { 798 if (old_workspace != new_workspace) {
948 container_remove_child(con); 799 container_detach(con);
949 workspace_add_floating(new_workspace, con); 800 workspace_add_floating(new_workspace, con);
950 arrange_windows(old_workspace); 801 arrange_workspace(old_workspace);
951 arrange_windows(new_workspace); 802 arrange_workspace(new_workspace);
952 workspace_detect_urgent(old_workspace); 803 workspace_detect_urgent(old_workspace);
953 workspace_detect_urgent(new_workspace); 804 workspace_detect_urgent(new_workspace);
954 } 805 }
@@ -959,22 +810,14 @@ void container_floating_move_to_center(struct sway_container *con) {
959 "Expected a floating container")) { 810 "Expected a floating container")) {
960 return; 811 return;
961 } 812 }
962 struct sway_container *ws = container_parent(con, C_WORKSPACE); 813 struct sway_workspace *ws = con->workspace;
963 double new_lx = ws->x + (ws->width - con->width) / 2; 814 double new_lx = ws->x + (ws->width - con->width) / 2;
964 double new_ly = ws->y + (ws->height - con->height) / 2; 815 double new_ly = ws->y + (ws->height - con->height) / 2;
965 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 816 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
966} 817}
967 818
968void container_set_dirty(struct sway_container *container) {
969 if (container->dirty) {
970 return;
971 }
972 container->dirty = true;
973 list_add(server.dirty_containers, container);
974}
975
976static bool find_urgent_iterator(struct sway_container *con, void *data) { 819static bool find_urgent_iterator(struct sway_container *con, void *data) {
977 return con->type == C_VIEW && view_is_urgent(con->sway_view); 820 return con->view && view_is_urgent(con->view);
978} 821}
979 822
980bool container_has_urgent_child(struct sway_container *container) { 823bool container_has_urgent_child(struct sway_container *container) {
@@ -991,12 +834,12 @@ void container_end_mouse_operation(struct sway_container *container) {
991} 834}
992 835
993static void set_fullscreen_iterator(struct sway_container *con, void *data) { 836static void set_fullscreen_iterator(struct sway_container *con, void *data) {
994 if (con->type != C_VIEW) { 837 if (!con->view) {
995 return; 838 return;
996 } 839 }
997 if (con->sway_view->impl->set_fullscreen) { 840 if (con->view->impl->set_fullscreen) {
998 bool *enable = data; 841 bool *enable = data;
999 con->sway_view->impl->set_fullscreen(con->sway_view, *enable); 842 con->view->impl->set_fullscreen(con->view, *enable);
1000 } 843 }
1001} 844}
1002 845
@@ -1005,9 +848,9 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1005 return; 848 return;
1006 } 849 }
1007 850
1008 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 851 struct sway_workspace *workspace = container->workspace;
1009 if (enable && workspace->sway_workspace->fullscreen) { 852 if (enable && workspace->fullscreen) {
1010 container_set_fullscreen(workspace->sway_workspace->fullscreen, false); 853 container_set_fullscreen(workspace->fullscreen, false);
1011 } 854 }
1012 855
1013 set_fullscreen_iterator(container, &enable); 856 set_fullscreen_iterator(container, &enable);
@@ -1016,36 +859,32 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1016 container->is_fullscreen = enable; 859 container->is_fullscreen = enable;
1017 860
1018 if (enable) { 861 if (enable) {
1019 workspace->sway_workspace->fullscreen = container; 862 workspace->fullscreen = container;
1020 container->saved_x = container->x; 863 container->saved_x = container->x;
1021 container->saved_y = container->y; 864 container->saved_y = container->y;
1022 container->saved_width = container->width; 865 container->saved_width = container->width;
1023 container->saved_height = container->height; 866 container->saved_height = container->height;
1024 867
1025 struct sway_seat *seat; 868 struct sway_seat *seat;
1026 struct sway_container *focus, *focus_ws; 869 struct sway_workspace *focus_ws;
1027 wl_list_for_each(seat, &input_manager->seats, link) { 870 wl_list_for_each(seat, &input_manager->seats, link) {
1028 focus = seat_get_focus(seat); 871 focus_ws = seat_get_focused_workspace(seat);
1029 if (focus) { 872 if (focus_ws) {
1030 focus_ws = focus;
1031 if (focus_ws->type != C_WORKSPACE) {
1032 focus_ws = container_parent(focus_ws, C_WORKSPACE);
1033 }
1034 if (focus_ws == workspace) { 873 if (focus_ws == workspace) {
1035 seat_set_focus(seat, container); 874 seat_set_focus(seat, &container->node);
1036 } 875 }
1037 } 876 }
1038 } 877 }
1039 } else { 878 } else {
1040 workspace->sway_workspace->fullscreen = NULL; 879 workspace->fullscreen = NULL;
1041 if (container_is_floating(container)) { 880 if (container_is_floating(container)) {
1042 container->x = container->saved_x; 881 container->x = container->saved_x;
1043 container->y = container->saved_y; 882 container->y = container->saved_y;
1044 container->width = container->saved_width; 883 container->width = container->saved_width;
1045 container->height = container->saved_height; 884 container->height = container->saved_height;
1046 struct sway_container *output = 885 struct sway_output *output =
1047 container_floating_find_output(container); 886 container_floating_find_output(container);
1048 if (!container_has_ancestor(container, output)) { 887 if (workspace->output != output) {
1049 container_floating_move_to_center(container); 888 container_floating_move_to_center(container);
1050 } 889 }
1051 } else { 890 } else {
@@ -1060,7 +899,7 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1060} 899}
1061 900
1062bool container_is_floating_or_child(struct sway_container *container) { 901bool container_is_floating_or_child(struct sway_container *container) {
1063 while (container->parent && container->parent->type != C_WORKSPACE) { 902 while (container->parent) {
1064 container = container->parent; 903 container = container->parent;
1065 } 904 }
1066 return container_is_floating(container); 905 return container_is_floating(container);
@@ -1072,7 +911,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1072 return true; 911 return true;
1073 } 912 }
1074 container = container->parent; 913 container = container->parent;
1075 } while (container && container->type != C_WORKSPACE); 914 } while (container);
1076 915
1077 return false; 916 return false;
1078} 917}
@@ -1090,42 +929,37 @@ static void surface_send_leave_iterator(struct wlr_surface *surface,
1090} 929}
1091 930
1092void container_discover_outputs(struct sway_container *con) { 931void container_discover_outputs(struct sway_container *con) {
1093 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
1094 "Expected a container or view")) {
1095 return;
1096 }
1097 struct wlr_box con_box = { 932 struct wlr_box con_box = {
1098 .x = con->current.swayc_x, 933 .x = con->current.con_x,
1099 .y = con->current.swayc_y, 934 .y = con->current.con_y,
1100 .width = con->current.swayc_width, 935 .width = con->current.con_width,
1101 .height = con->current.swayc_height, 936 .height = con->current.con_height,
1102 }; 937 };
1103 struct sway_output *old_output = container_get_effective_output(con); 938 struct sway_output *old_output = container_get_effective_output(con);
1104 939
1105 for (int i = 0; i < root_container.children->length; ++i) { 940 for (int i = 0; i < root->outputs->length; ++i) {
1106 struct sway_container *output = root_container.children->items[i]; 941 struct sway_output *output = root->outputs->items[i];
1107 struct sway_output *sway_output = output->sway_output;
1108 struct wlr_box output_box; 942 struct wlr_box output_box;
1109 container_get_box(output, &output_box); 943 output_get_box(output, &output_box);
1110 struct wlr_box intersection; 944 struct wlr_box intersection;
1111 bool intersects = 945 bool intersects =
1112 wlr_box_intersection(&con_box, &output_box, &intersection); 946 wlr_box_intersection(&con_box, &output_box, &intersection);
1113 int index = list_find(con->outputs, sway_output); 947 int index = list_find(con->outputs, output);
1114 948
1115 if (intersects && index == -1) { 949 if (intersects && index == -1) {
1116 // Send enter 950 // Send enter
1117 wlr_log(WLR_DEBUG, "Con %p entered output %p", con, sway_output); 951 wlr_log(WLR_DEBUG, "Container %p entered output %p", con, output);
1118 if (con->type == C_VIEW) { 952 if (con->view) {
1119 view_for_each_surface(con->sway_view, 953 view_for_each_surface(con->view,
1120 surface_send_enter_iterator, sway_output->wlr_output); 954 surface_send_enter_iterator, output->wlr_output);
1121 } 955 }
1122 list_add(con->outputs, sway_output); 956 list_add(con->outputs, output);
1123 } else if (!intersects && index != -1) { 957 } else if (!intersects && index != -1) {
1124 // Send leave 958 // Send leave
1125 wlr_log(WLR_DEBUG, "Con %p left output %p", con, sway_output); 959 wlr_log(WLR_DEBUG, "Container %p left output %p", con, output);
1126 if (con->type == C_VIEW) { 960 if (con->view) {
1127 view_for_each_surface(con->sway_view, 961 view_for_each_surface(con->view,
1128 surface_send_leave_iterator, sway_output->wlr_output); 962 surface_send_leave_iterator, output->wlr_output);
1129 } 963 }
1130 list_del(con->outputs, index); 964 list_del(con->outputs, index);
1131 } 965 }
@@ -1135,17 +969,13 @@ void container_discover_outputs(struct sway_container *con) {
1135 double new_scale = new_output ? new_output->wlr_output->scale : -1; 969 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1136 if (old_scale != new_scale) { 970 if (old_scale != new_scale) {
1137 container_update_title_textures(con); 971 container_update_title_textures(con);
1138 if (con->type == C_VIEW) { 972 if (con->view) {
1139 view_update_marks_textures(con->sway_view); 973 view_update_marks_textures(con->view);
1140 } 974 }
1141 } 975 }
1142} 976}
1143 977
1144void container_remove_gaps(struct sway_container *c) { 978void container_remove_gaps(struct sway_container *c) {
1145 if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW,
1146 "Expected a container or view")) {
1147 return;
1148 }
1149 if (c->current_gaps == 0) { 979 if (c->current_gaps == 0) {
1150 return; 980 return;
1151 } 981 }
@@ -1158,25 +988,20 @@ void container_remove_gaps(struct sway_container *c) {
1158} 988}
1159 989
1160void container_add_gaps(struct sway_container *c) { 990void container_add_gaps(struct sway_container *c) {
1161 if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW,
1162 "Expected a container or view")) {
1163 return;
1164 }
1165 if (c->current_gaps > 0) { 991 if (c->current_gaps > 0) {
1166 return; 992 return;
1167 } 993 }
1168 // Linear containers don't have gaps because it'd create double gaps 994 // Linear containers don't have gaps because it'd create double gaps
1169 if (c->type == C_CONTAINER && 995 if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) {
1170 c->layout != L_TABBED && c->layout != L_STACKED) {
1171 return; 996 return;
1172 } 997 }
1173 // Children of tabbed/stacked containers re-use the gaps of the container 998 // Children of tabbed/stacked containers re-use the gaps of the container
1174 enum sway_container_layout layout = c->parent->layout; 999 enum sway_container_layout layout = container_parent_layout(c);
1175 if (layout == L_TABBED || layout == L_STACKED) { 1000 if (layout == L_TABBED || layout == L_STACKED) {
1176 return; 1001 return;
1177 } 1002 }
1178 1003
1179 struct sway_container *ws = container_parent(c, C_WORKSPACE); 1004 struct sway_workspace *ws = c->workspace;
1180 1005
1181 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 1006 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner;
1182 c->x += c->current_gaps; 1007 c->x += c->current_gaps;
@@ -1185,222 +1010,154 @@ void container_add_gaps(struct sway_container *c) {
1185 c->height -= 2 * c->current_gaps; 1010 c->height -= 2 * c->current_gaps;
1186} 1011}
1187 1012
1188int container_sibling_index(const struct sway_container *child) { 1013enum sway_container_layout container_parent_layout(struct sway_container *con) {
1189 return list_find(child->parent->children, child); 1014 if (con->parent) {
1015 return con->parent->layout;
1016 }
1017 return con->workspace->layout;
1190} 1018}
1191 1019
1192void container_handle_fullscreen_reparent(struct sway_container *con, 1020enum sway_container_layout container_current_parent_layout(
1193 struct sway_container *old_parent) { 1021 struct sway_container *con) {
1194 if (!con->is_fullscreen) { 1022 if (con->current.parent) {
1195 return; 1023 return con->current.parent->current.layout;
1196 } 1024 }
1197 struct sway_container *old_workspace = old_parent; 1025 return con->current.workspace->current.layout;
1198 if (old_workspace && old_workspace->type != C_WORKSPACE) { 1026}
1199 old_workspace = container_parent(old_workspace, C_WORKSPACE); 1027
1028list_t *container_get_siblings(const struct sway_container *container) {
1029 if (container->parent) {
1030 return container->parent->children;
1200 } 1031 }
1201 struct sway_container *new_workspace = container_parent(con, C_WORKSPACE); 1032 if (!container->workspace) {
1202 if (old_workspace == new_workspace) { 1033 return NULL;
1203 return;
1204 } 1034 }
1205 // Unmark the old workspace as fullscreen 1035 if (list_find(container->workspace->tiling, container) != -1) {
1206 if (old_workspace) { 1036 return container->workspace->tiling;
1207 old_workspace->sway_workspace->fullscreen = NULL;
1208 } 1037 }
1038 return container->workspace->floating;
1039}
1209 1040
1210 // Mark the new workspace as fullscreen 1041int container_sibling_index(const struct sway_container *child) {
1211 if (new_workspace->sway_workspace->fullscreen) { 1042 return list_find(container_get_siblings(child), child);
1212 container_set_fullscreen( 1043}
1213 new_workspace->sway_workspace->fullscreen, false);
1214 }
1215 new_workspace->sway_workspace->fullscreen = con;
1216 1044
1217 // Resize container to new output dimensions 1045list_t *container_get_current_siblings(struct sway_container *container) {
1218 struct sway_container *output = new_workspace->parent; 1046 if (container->current.parent) {
1219 con->x = output->x; 1047 return container->current.parent->current.children;
1220 con->y = output->y; 1048 }
1221 con->width = output->width; 1049 return container->current.workspace->current.tiling;
1222 con->height = output->height; 1050}
1223 1051
1224 if (con->type == C_VIEW) { 1052void container_handle_fullscreen_reparent(struct sway_container *con) {
1225 struct sway_view *view = con->sway_view; 1053 if (!con->is_fullscreen || con->workspace->fullscreen == con) {
1226 view->x = output->x; 1054 return;
1227 view->y = output->y;
1228 view->width = output->width;
1229 view->height = output->height;
1230 } else {
1231 arrange_windows(new_workspace);
1232 } 1055 }
1056 if (con->workspace->fullscreen) {
1057 container_set_fullscreen(con->workspace->fullscreen, false);
1058 }
1059 con->workspace->fullscreen = con;
1060
1061 arrange_workspace(con->workspace);
1062}
1063
1064static void set_workspace(struct sway_container *container, void *data) {
1065 container->workspace = container->parent->workspace;
1233} 1066}
1234 1067
1235void container_insert_child(struct sway_container *parent, 1068void container_insert_child(struct sway_container *parent,
1236 struct sway_container *child, int i) { 1069 struct sway_container *child, int i) {
1237 struct sway_container *old_parent = child->parent; 1070 if (child->workspace) {
1238 if (old_parent) { 1071 container_detach(child);
1239 container_remove_child(child);
1240 } 1072 }
1241 wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i);
1242 list_insert(parent->children, i, child); 1073 list_insert(parent->children, i, child);
1243 child->parent = parent; 1074 child->parent = parent;
1244 container_handle_fullscreen_reparent(child, old_parent); 1075 child->workspace = parent->workspace;
1076 container_for_each_child(child, set_workspace, NULL);
1077 container_handle_fullscreen_reparent(child);
1078 container_update_representation(parent);
1245} 1079}
1246 1080
1247struct sway_container *container_add_sibling(struct sway_container *fixed, 1081void container_add_sibling(struct sway_container *fixed,
1248 struct sway_container *active) { 1082 struct sway_container *active, int offset) {
1249 // TODO handle floating 1083 if (active->workspace) {
1250 struct sway_container *old_parent = NULL; 1084 container_detach(active);
1251 if (active->parent) { 1085 }
1252 old_parent = active->parent; 1086 list_t *siblings = container_get_siblings(fixed);
1253 container_remove_child(active); 1087 int index = list_find(siblings, fixed);
1254 } 1088 list_insert(siblings, index + offset, active);
1255 struct sway_container *parent = fixed->parent; 1089 active->parent = fixed->parent;
1256 int i = container_sibling_index(fixed); 1090 active->workspace = fixed->workspace;
1257 list_insert(parent->children, i + 1, active); 1091 container_for_each_child(active, set_workspace, NULL);
1258 active->parent = parent; 1092 container_handle_fullscreen_reparent(active);
1259 container_handle_fullscreen_reparent(active, old_parent); 1093 container_update_representation(active);
1260 return active->parent;
1261} 1094}
1262 1095
1263void container_add_child(struct sway_container *parent, 1096void container_add_child(struct sway_container *parent,
1264 struct sway_container *child) { 1097 struct sway_container *child) {
1265 wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", 1098 if (child->workspace) {
1266 child, child->type, child->width, child->height, 1099 container_detach(child);
1267 parent, parent->type, parent->width, parent->height); 1100 }
1268 struct sway_container *old_parent = child->parent;
1269 list_add(parent->children, child); 1101 list_add(parent->children, child);
1270 child->parent = parent; 1102 child->parent = parent;
1271 container_handle_fullscreen_reparent(child, old_parent); 1103 child->workspace = parent->workspace;
1272 if (old_parent) { 1104 container_for_each_child(child, set_workspace, NULL);
1273 container_set_dirty(old_parent); 1105 container_handle_fullscreen_reparent(child);
1274 } 1106 container_update_representation(parent);
1275 container_set_dirty(child); 1107 node_set_dirty(&child->node);
1108 node_set_dirty(&parent->node);
1276} 1109}
1277 1110
1278struct sway_container *container_remove_child(struct sway_container *child) { 1111void container_detach(struct sway_container *child) {
1279 if (child->is_fullscreen) { 1112 if (child->is_fullscreen) {
1280 struct sway_container *workspace = container_parent(child, C_WORKSPACE); 1113 child->workspace->fullscreen = NULL;
1281 workspace->sway_workspace->fullscreen = NULL;
1282 } 1114 }
1283 1115
1284 struct sway_container *parent = child->parent; 1116 struct sway_container *old_parent = child->parent;
1285 list_t *list = container_is_floating(child) ? 1117 struct sway_workspace *old_workspace = child->workspace;
1286 parent->sway_workspace->floating : parent->children; 1118 list_t *siblings = container_get_siblings(child);
1287 int index = list_find(list, child); 1119 int index = list_find(siblings, child);
1288 if (index != -1) { 1120 if (index != -1) {
1289 list_del(list, index); 1121 list_del(siblings, index);
1290 } 1122 }
1291 child->parent = NULL; 1123 child->parent = NULL;
1292 container_notify_subtree_changed(parent); 1124 child->workspace = NULL;
1293 1125 container_for_each_child(child, set_workspace, NULL);
1294 container_set_dirty(parent);
1295 container_set_dirty(child);
1296
1297 return parent;
1298}
1299
1300enum sway_container_layout container_get_default_layout(
1301 struct sway_container *con) {
1302 if (con->type != C_OUTPUT) {
1303 con = container_parent(con, C_OUTPUT);
1304 }
1305 1126
1306 if (!sway_assert(con != NULL, 1127 if (old_parent) {
1307 "container_get_default_layout must be called on an attached" 1128 container_update_representation(old_parent);
1308 " container below the root container")) { 1129 node_set_dirty(&old_parent->node);
1309 return 0;
1310 }
1311
1312 if (config->default_layout != L_NONE) {
1313 return config->default_layout;
1314 } else if (config->default_orientation != L_NONE) {
1315 return config->default_orientation;
1316 } else if (con->width >= con->height) {
1317 return L_HORIZ;
1318 } else { 1130 } else {
1319 return L_VERT; 1131 workspace_update_representation(old_workspace);
1132 node_set_dirty(&old_workspace->node);
1320 } 1133 }
1134 node_set_dirty(&child->node);
1321} 1135}
1322 1136
1323struct sway_container *container_replace_child(struct sway_container *child, 1137void container_replace(struct sway_container *container,
1324 struct sway_container *new_child) { 1138 struct sway_container *replacement) {
1325 struct sway_container *parent = child->parent; 1139 container_add_sibling(container, replacement, 1);
1326 if (parent == NULL) { 1140 container_detach(container);
1327 return NULL;
1328 }
1329
1330 list_t *list = container_is_floating(child) ?
1331 parent->sway_workspace->floating : parent->children;
1332 int i = list_find(list, child);
1333
1334 if (new_child->parent) {
1335 container_remove_child(new_child);
1336 }
1337 list->items[i] = new_child;
1338 new_child->parent = parent;
1339 child->parent = NULL;
1340
1341 // Set geometry for new child
1342 new_child->x = child->x;
1343 new_child->y = child->y;
1344 new_child->width = child->width;
1345 new_child->height = child->height;
1346
1347 // reset geometry for child
1348 child->width = 0;
1349 child->height = 0;
1350
1351 return parent;
1352} 1141}
1353 1142
1354struct sway_container *container_split(struct sway_container *child, 1143struct sway_container *container_split(struct sway_container *child,
1355 enum sway_container_layout layout) { 1144 enum sway_container_layout layout) {
1356 // TODO floating: cannot split a floating container 1145 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
1357 if (!sway_assert(child, "child cannot be null")) { 1146 bool set_focus = (seat_get_focus(seat) == &child->node);
1358 return NULL;
1359 }
1360 if (child->type == C_WORKSPACE && child->children->length == 0) {
1361 // Special case: this just behaves like splitt
1362 child->prev_split_layout = child->layout;
1363 child->layout = layout;
1364 return child;
1365 }
1366
1367 struct sway_container *cont = container_create(C_CONTAINER);
1368
1369 wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child);
1370 1147
1371 cont->prev_split_layout = L_NONE; 1148 struct sway_container *cont = container_create(NULL);
1372 cont->width = child->width; 1149 cont->width = child->width;
1373 cont->height = child->height; 1150 cont->height = child->height;
1374 cont->x = child->x;
1375 cont->y = child->y;
1376 cont->current_gaps = child->current_gaps; 1151 cont->current_gaps = child->current_gaps;
1152 cont->layout = layout;
1377 1153
1378 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 1154 container_replace(child, cont);
1379 bool set_focus = (seat_get_focus(seat) == child); 1155 container_add_child(cont, child);
1380
1381 if (child->type == C_WORKSPACE) {
1382 struct sway_container *workspace = child;
1383 while (workspace->children->length) {
1384 struct sway_container *ws_child = workspace->children->items[0];
1385 container_remove_child(ws_child);
1386 container_add_child(cont, ws_child);
1387 }
1388
1389 container_add_child(workspace, cont);
1390 enum sway_container_layout old_layout = workspace->layout;
1391 workspace->layout = layout;
1392 cont->layout = old_layout;
1393 } else {
1394 cont->layout = layout;
1395 container_replace_child(child, cont);
1396 container_add_child(cont, child);
1397 }
1398 1156
1399 if (set_focus) { 1157 if (set_focus) {
1400 seat_set_focus(seat, cont); 1158 seat_set_focus(seat, &cont->node);
1401 seat_set_focus(seat, child); 1159 seat_set_focus(seat, &child->node);
1402 } 1160 }
1403 1161
1404 container_notify_subtree_changed(cont);
1405 return cont; 1162 return cont;
1406} 1163}
diff --git a/sway/tree/node.c b/sway/tree/node.c
new file mode 100644
index 00000000..74661c1a
--- /dev/null
+++ b/sway/tree/node.c
@@ -0,0 +1,151 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h"
3#include "sway/server.h"
4#include "sway/tree/container.h"
5#include "sway/tree/node.h"
6#include "sway/tree/root.h"
7#include "sway/tree/workspace.h"
8#include "log.h"
9
10void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
11 static size_t next_id = 1;
12 node->id = next_id++;
13 node->type = type;
14 node->sway_root = thing;
15 wl_signal_init(&node->events.destroy);
16}
17
18const char *node_type_to_str(enum sway_node_type type) {
19 switch (type) {
20 case N_ROOT:
21 return "N_ROOT";
22 case N_OUTPUT:
23 return "N_OUTPUT";
24 case N_WORKSPACE:
25 return "N_WORKSPACE";
26 case N_CONTAINER:
27 return "N_CONTAINER";
28 }
29 return "";
30}
31
32void node_set_dirty(struct sway_node *node) {
33 if (node->dirty) {
34 return;
35 }
36 node->dirty = true;
37 list_add(server.dirty_nodes, node);
38}
39
40bool node_is_view(struct sway_node *node) {
41 return node->type == N_CONTAINER && node->sway_container->view;
42}
43
44char *node_get_name(struct sway_node *node) {
45 switch (node->type) {
46 case N_ROOT:
47 return "root";
48 case N_OUTPUT:
49 return node->sway_output->wlr_output->name;
50 case N_WORKSPACE:
51 return node->sway_workspace->name;
52 case N_CONTAINER:
53 return node->sway_container->title;
54 }
55 return NULL;
56}
57
58void node_get_box(struct sway_node *node, struct wlr_box *box) {
59 switch (node->type) {
60 case N_ROOT:
61 root_get_box(root, box);
62 break;
63 case N_OUTPUT:
64 output_get_box(node->sway_output, box);
65 break;
66 case N_WORKSPACE:
67 workspace_get_box(node->sway_workspace, box);
68 break;
69 case N_CONTAINER:
70 container_get_box(node->sway_container, box);
71 break;
72 }
73}
74
75struct sway_output *node_get_output(struct sway_node *node) {
76 switch (node->type) {
77 case N_CONTAINER:
78 return node->sway_container->workspace->output;
79 case N_WORKSPACE:
80 return node->sway_workspace->output;
81 case N_OUTPUT:
82 return node->sway_output;
83 case N_ROOT:
84 return NULL;
85 }
86 return NULL;
87}
88
89enum sway_container_layout node_get_layout(struct sway_node *node) {
90 switch (node->type) {
91 case N_CONTAINER:
92 return node->sway_container->layout;
93 case N_WORKSPACE:
94 return node->sway_workspace->layout;
95 case N_OUTPUT:
96 case N_ROOT:
97 return L_NONE;
98 }
99 return L_NONE;
100}
101
102struct sway_node *node_get_parent(struct sway_node *node) {
103 switch (node->type) {
104 case N_CONTAINER: {
105 struct sway_container *con = node->sway_container;
106 if (con->parent) {
107 return &con->parent->node;
108 }
109 if (con->workspace) {
110 return &con->workspace->node;
111 }
112 }
113 return NULL;
114 case N_WORKSPACE: {
115 struct sway_workspace *ws = node->sway_workspace;
116 if (ws->output) {
117 return &ws->output->node;
118 }
119 }
120 return NULL;
121 case N_OUTPUT:
122 return &root->node;
123 case N_ROOT:
124 return NULL;
125 }
126 return NULL;
127}
128
129list_t *node_get_children(struct sway_node *node) {
130 switch (node->type) {
131 case N_CONTAINER:
132 return node->sway_container->children;
133 case N_WORKSPACE:
134 return node->sway_workspace->tiling;
135 case N_OUTPUT:
136 case N_ROOT:
137 return NULL;
138 }
139 return NULL;
140}
141
142bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
143 struct sway_node *parent = node_get_parent(node);
144 while (parent) {
145 if (parent == ancestor) {
146 return true;
147 }
148 parent = node_get_parent(parent);
149 }
150 return false;
151}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 6601220b..d72eb1a1 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -2,28 +2,31 @@
2#include <ctype.h> 2#include <ctype.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
5#include <wlr/types/wlr_output_damage.h>
5#include "sway/ipc-server.h" 6#include "sway/ipc-server.h"
7#include "sway/layers.h"
6#include "sway/output.h" 8#include "sway/output.h"
7#include "sway/tree/arrange.h" 9#include "sway/tree/arrange.h"
8#include "sway/tree/output.h" 10#include "sway/tree/output.h"
9#include "sway/tree/workspace.h" 11#include "sway/tree/workspace.h"
10#include "log.h" 12#include "log.h"
13#include "util.h"
11 14
12static void restore_workspaces(struct sway_container *output) { 15static void restore_workspaces(struct sway_output *output) {
13 // Workspace output priority 16 // Workspace output priority
14 for (int i = 0; i < root_container.children->length; i++) { 17 for (int i = 0; i < root->outputs->length; i++) {
15 struct sway_container *other = root_container.children->items[i]; 18 struct sway_output *other = root->outputs->items[i];
16 if (other == output) { 19 if (other == output) {
17 continue; 20 continue;
18 } 21 }
19 22
20 for (int j = 0; j < other->children->length; j++) { 23 for (int j = 0; j < other->workspaces->length; j++) {
21 struct sway_container *ws = other->children->items[j]; 24 struct sway_workspace *ws = other->workspaces->items[j];
22 struct sway_container *highest = 25 struct sway_output *highest =
23 workspace_output_get_highest_available(ws, NULL); 26 workspace_output_get_highest_available(ws, NULL);
24 if (highest == output) { 27 if (highest == output) {
25 container_remove_child(ws); 28 workspace_detach(ws);
26 container_add_child(output, ws); 29 output_add_workspace(output, ws);
27 ipc_event_workspace(NULL, ws, "move"); 30 ipc_event_workspace(NULL, ws, "move");
28 j--; 31 j--;
29 } 32 }
@@ -31,111 +34,111 @@ static void restore_workspaces(struct sway_container *output) {
31 } 34 }
32 35
33 // Saved workspaces 36 // Saved workspaces
34 list_t *saved = root_container.sway_root->saved_workspaces; 37 for (int i = 0; i < root->saved_workspaces->length; ++i) {
35 for (int i = 0; i < saved->length; ++i) { 38 struct sway_workspace *ws = root->saved_workspaces->items[i];
36 struct sway_container *ws = saved->items[i]; 39 output_add_workspace(output, ws);
37 container_add_child(output, ws);
38 ipc_event_workspace(NULL, ws, "move"); 40 ipc_event_workspace(NULL, ws, "move");
39 } 41 }
40 saved->length = 0; 42 root->saved_workspaces->length = 0;
41 43
42 output_sort_workspaces(output); 44 output_sort_workspaces(output);
43} 45}
44 46
45struct sway_container *output_create( 47struct sway_output *output_create(struct wlr_output *wlr_output) {
46 struct sway_output *sway_output) { 48 struct sway_output *output = calloc(1, sizeof(struct sway_output));
47 const char *name = sway_output->wlr_output->name; 49 node_init(&output->node, N_OUTPUT, output);
48 char identifier[128]; 50 output->wlr_output = wlr_output;
49 output_get_identifier(identifier, sizeof(identifier), sway_output); 51 wlr_output->data = output;
50 52
51 struct output_config *oc = NULL, *all = NULL; 53 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
52 for (int i = 0; i < config->output_configs->length; ++i) {
53 struct output_config *cur = config->output_configs->items[i];
54 54
55 if (strcasecmp(name, cur->name) == 0 || 55 wl_list_insert(&root->all_outputs, &output->link);
56 strcasecmp(identifier, cur->name) == 0) {
57 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
58 oc = cur;
59 }
60 if (strcasecmp("*", cur->name) == 0) {
61 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
62 all = cur;
63 }
64 56
65 if (oc && all) { 57 if (!wl_list_empty(&wlr_output->modes)) {
66 break; 58 struct wlr_output_mode *mode =
67 } 59 wl_container_of(wlr_output->modes.prev, mode, link);
68 } 60 wlr_output_set_mode(wlr_output, mode);
69 if (!oc) {
70 oc = all;
71 } 61 }
72 62
73 if (oc && !oc->enabled) { 63 output->workspaces = create_list();
74 return NULL; 64 output->current.workspaces = create_list();
75 }
76 65
77 struct sway_container *output = container_create(C_OUTPUT); 66 return output;
78 output->sway_output = sway_output; 67}
79 output->name = strdup(name);
80 if (output->name == NULL) {
81 output_begin_destroy(output);
82 return NULL;
83 }
84 68
69void output_enable(struct sway_output *output, struct output_config *oc) {
70 if (!sway_assert(!output->enabled, "output is already enabled")) {
71 return;
72 }
73 struct wlr_output *wlr_output = output->wlr_output;
74 output->enabled = true;
85 apply_output_config(oc, output); 75 apply_output_config(oc, output);
86 container_add_child(&root_container, output); 76 list_add(root->outputs, output);
87 load_swaybars();
88
89 struct wlr_box size;
90 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
91 &size.height);
92 output->width = size.width;
93 output->height = size.height;
94 77
95 restore_workspaces(output); 78 restore_workspaces(output);
96 79
97 if (!output->children->length) { 80 if (!output->workspaces->length) {
98 // Create workspace 81 // Create workspace
99 char *ws_name = workspace_next_name(output->name); 82 char *ws_name = workspace_next_name(wlr_output->name);
100 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); 83 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
101 struct sway_container *ws = workspace_create(output, ws_name); 84 struct sway_workspace *ws = workspace_create(output, ws_name);
102 // Set each seat's focus if not already set 85 // Set each seat's focus if not already set
103 struct sway_seat *seat = NULL; 86 struct sway_seat *seat = NULL;
104 wl_list_for_each(seat, &input_manager->seats, link) { 87 wl_list_for_each(seat, &input_manager->seats, link) {
105 if (!seat->has_focus) { 88 if (!seat->has_focus) {
106 seat_set_focus(seat, ws); 89 seat_set_focus(seat, &ws->node);
107 } 90 }
108 } 91 }
109 free(ws_name); 92 free(ws_name);
110 } 93 }
111 94
112 container_create_notify(output); 95 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
113 return output; 96 for (size_t i = 0; i < len; ++i) {
97 wl_list_init(&output->layers[i]);
98 }
99 wl_signal_init(&output->events.destroy);
100
101 input_manager_configure_xcursor(input_manager);
102
103 wl_signal_add(&wlr_output->events.mode, &output->mode);
104 wl_signal_add(&wlr_output->events.transform, &output->transform);
105 wl_signal_add(&wlr_output->events.scale, &output->scale);
106 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
107 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
108
109 output_add_listeners(output);
110
111 wl_signal_emit(&root->events.new_node, &output->node);
112
113 load_swaybars();
114
115 arrange_layers(output);
116 arrange_root();
114} 117}
115 118
116static void output_evacuate(struct sway_container *output) { 119static void output_evacuate(struct sway_output *output) {
117 if (!output->children->length) { 120 if (!output->workspaces->length) {
118 return; 121 return;
119 } 122 }
120 struct sway_container *fallback_output = NULL; 123 struct sway_output *fallback_output = NULL;
121 if (root_container.children->length > 1) { 124 if (root->outputs->length > 1) {
122 fallback_output = root_container.children->items[0]; 125 fallback_output = root->outputs->items[0];
123 if (fallback_output == output) { 126 if (fallback_output == output) {
124 fallback_output = root_container.children->items[1]; 127 fallback_output = root->outputs->items[1];
125 } 128 }
126 } 129 }
127 130
128 while (output->children->length) { 131 while (output->workspaces->length) {
129 struct sway_container *workspace = output->children->items[0]; 132 struct sway_workspace *workspace = output->workspaces->items[0];
130 133
131 container_remove_child(workspace); 134 workspace_detach(workspace);
132 135
133 if (workspace_is_empty(workspace)) { 136 if (workspace_is_empty(workspace)) {
134 workspace_begin_destroy(workspace); 137 workspace_begin_destroy(workspace);
135 continue; 138 continue;
136 } 139 }
137 140
138 struct sway_container *new_output = 141 struct sway_output *new_output =
139 workspace_output_get_highest_available(workspace, output); 142 workspace_output_get_highest_available(workspace, output);
140 if (!new_output) { 143 if (!new_output) {
141 new_output = fallback_output; 144 new_output = fallback_output;
@@ -143,39 +146,31 @@ static void output_evacuate(struct sway_container *output) {
143 146
144 if (new_output) { 147 if (new_output) {
145 workspace_output_add_priority(workspace, new_output); 148 workspace_output_add_priority(workspace, new_output);
146 container_add_child(new_output, workspace); 149 output_add_workspace(new_output, workspace);
147 output_sort_workspaces(new_output); 150 output_sort_workspaces(new_output);
148 ipc_event_workspace(NULL, workspace, "move"); 151 ipc_event_workspace(NULL, workspace, "move");
149 } else { 152 } else {
150 list_add(root_container.sway_root->saved_workspaces, workspace); 153 list_add(root->saved_workspaces, workspace);
151 } 154 }
152 } 155 }
153} 156}
154 157
155void output_destroy(struct sway_container *output) { 158void output_destroy(struct sway_output *output) {
156 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 159 if (!sway_assert(output->node.destroying,
160 "Tried to free output which wasn't marked as destroying")) {
157 return; 161 return;
158 } 162 }
159 if (!sway_assert(output->destroying, 163 if (!sway_assert(output->wlr_output == NULL,
160 "Tried to free output which wasn't marked as destroying")) { 164 "Tried to free output which still had a wlr_output")) {
161 return; 165 return;
162 } 166 }
163 if (!sway_assert(output->ntxnrefs == 0, "Tried to free output " 167 if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output "
164 "which is still referenced by transactions")) { 168 "which is still referenced by transactions")) {
165 return; 169 return;
166 } 170 }
167 free(output->name); 171 list_free(output->workspaces);
168 free(output->formatted_title); 172 list_free(output->current.workspaces);
169 wlr_texture_destroy(output->title_focused);
170 wlr_texture_destroy(output->title_focused_inactive);
171 wlr_texture_destroy(output->title_unfocused);
172 wlr_texture_destroy(output->title_urgent);
173 list_free(output->children);
174 list_free(output->current.children);
175 list_free(output->outputs);
176 free(output); 173 free(output);
177
178 // NOTE: We don't actually destroy the sway_output here
179} 174}
180 175
181static void untrack_output(struct sway_container *con, void *data) { 176static void untrack_output(struct sway_container *con, void *data) {
@@ -186,76 +181,131 @@ static void untrack_output(struct sway_container *con, void *data) {
186 } 181 }
187} 182}
188 183
189void output_begin_destroy(struct sway_container *output) { 184void output_disable(struct sway_output *output) {
190 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 185 if (!sway_assert(output->enabled, "Expected an enabled output")) {
191 return; 186 return;
192 } 187 }
193 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 188 wlr_log(WLR_DEBUG, "Disabling output '%s'", output->wlr_output->name);
194 wl_signal_emit(&output->events.destroy, output); 189 wl_signal_emit(&output->events.destroy, output);
195 190
196 output_evacuate(output); 191 output_evacuate(output);
197 192
198 output->destroying = true; 193 root_for_each_container(untrack_output, output);
199 container_set_dirty(output); 194
195 int index = list_find(root->outputs, output);
196 list_del(root->outputs, index);
200 197
201 root_for_each_container(untrack_output, output->sway_output); 198 wl_list_remove(&output->mode.link);
199 wl_list_remove(&output->transform.link);
200 wl_list_remove(&output->scale.link);
201 wl_list_remove(&output->damage_destroy.link);
202 wl_list_remove(&output->damage_frame.link);
202 203
203 wl_list_remove(&output->sway_output->mode.link); 204 output->enabled = false;
204 wl_list_remove(&output->sway_output->transform.link);
205 wl_list_remove(&output->sway_output->scale.link);
206 wl_list_remove(&output->sway_output->damage_destroy.link);
207 wl_list_remove(&output->sway_output->damage_frame.link);
208 205
209 output->sway_output->swayc = NULL; 206 arrange_root();
210 output->sway_output = NULL; 207}
211 208
212 if (output->parent) { 209void output_begin_destroy(struct sway_output *output) {
213 container_remove_child(output); 210 if (!sway_assert(!output->enabled, "Expected a disabled output")) {
211 return;
214 } 212 }
213 wlr_log(WLR_DEBUG, "Destroying output '%s'", output->wlr_output->name);
214
215 output->node.destroying = true;
216 node_set_dirty(&output->node);
217
218 wl_list_remove(&output->link);
219 wl_list_remove(&output->destroy.link);
220 output->wlr_output->data = NULL;
221 output->wlr_output = NULL;
215} 222}
216 223
217struct sway_container *output_from_wlr_output(struct wlr_output *output) { 224struct output_config *output_find_config(struct sway_output *output) {
218 if (output == NULL) { 225 const char *name = output->wlr_output->name;
226 char identifier[128];
227 output_get_identifier(identifier, sizeof(identifier), output);
228
229 struct output_config *oc = NULL, *all = NULL;
230 for (int i = 0; i < config->output_configs->length; ++i) {
231 struct output_config *cur = config->output_configs->items[i];
232
233 if (strcasecmp(name, cur->name) == 0 ||
234 strcasecmp(identifier, cur->name) == 0) {
235 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
236 oc = cur;
237 }
238 if (strcasecmp("*", cur->name) == 0) {
239 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
240 all = cur;
241 }
242
243 if (oc && all) {
244 break;
245 }
246 }
247 if (!oc) {
248 oc = all;
249 }
250
251 if (oc && !oc->enabled) {
219 return NULL; 252 return NULL;
220 } 253 }
221 for (int i = 0; i < root_container.children->length; ++i) { 254 return oc;
222 struct sway_container *o = root_container.children->items[i]; 255}
223 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { 256
224 return o; 257struct sway_output *output_from_wlr_output(struct wlr_output *output) {
225 } 258 return output->data;
259}
260
261struct sway_output *output_get_in_direction(struct sway_output *reference,
262 enum movement_direction direction) {
263 enum wlr_direction wlr_dir = 0;
264 if (!sway_assert(sway_dir_to_wlr(direction, &wlr_dir),
265 "got invalid direction: %d", direction)) {
266 return NULL;
226 } 267 }
227 return NULL; 268 int lx = reference->wlr_output->lx + reference->wlr_output->width / 2;
269 int ly = reference->wlr_output->ly + reference->wlr_output->height / 2;
270 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
271 root->output_layout, wlr_dir, reference->wlr_output, lx, ly);
272 if (!wlr_adjacent) {
273 return NULL;
274 }
275 return output_from_wlr_output(wlr_adjacent);
228} 276}
229 277
230void output_for_each_workspace(struct sway_container *output, 278void output_add_workspace(struct sway_output *output,
231 void (*f)(struct sway_container *con, void *data), void *data) { 279 struct sway_workspace *workspace) {
232 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 280 if (workspace->output) {
233 return; 281 workspace_detach(workspace);
234 } 282 }
235 for (int i = 0; i < output->children->length; ++i) { 283 list_add(output->workspaces, workspace);
236 struct sway_container *workspace = output->children->items[i]; 284 workspace->output = output;
285 node_set_dirty(&output->node);
286 node_set_dirty(&workspace->node);
287}
288
289void output_for_each_workspace(struct sway_output *output,
290 void (*f)(struct sway_workspace *ws, void *data), void *data) {
291 for (int i = 0; i < output->workspaces->length; ++i) {
292 struct sway_workspace *workspace = output->workspaces->items[i];
237 f(workspace, data); 293 f(workspace, data);
238 } 294 }
239} 295}
240 296
241void output_for_each_container(struct sway_container *output, 297void output_for_each_container(struct sway_output *output,
242 void (*f)(struct sway_container *con, void *data), void *data) { 298 void (*f)(struct sway_container *con, void *data), void *data) {
243 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 299 for (int i = 0; i < output->workspaces->length; ++i) {
244 return; 300 struct sway_workspace *workspace = output->workspaces->items[i];
245 }
246 for (int i = 0; i < output->children->length; ++i) {
247 struct sway_container *workspace = output->children->items[i];
248 workspace_for_each_container(workspace, f, data); 301 workspace_for_each_container(workspace, f, data);
249 } 302 }
250} 303}
251 304
252struct sway_container *output_find_workspace(struct sway_container *output, 305struct sway_workspace *output_find_workspace(struct sway_output *output,
253 bool (*test)(struct sway_container *con, void *data), void *data) { 306 bool (*test)(struct sway_workspace *ws, void *data), void *data) {
254 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 307 for (int i = 0; i < output->workspaces->length; ++i) {
255 return NULL; 308 struct sway_workspace *workspace = output->workspaces->items[i];
256 }
257 for (int i = 0; i < output->children->length; ++i) {
258 struct sway_container *workspace = output->children->items[i];
259 if (test(workspace, data)) { 309 if (test(workspace, data)) {
260 return workspace; 310 return workspace;
261 } 311 }
@@ -263,14 +313,11 @@ struct sway_container *output_find_workspace(struct sway_container *output,
263 return NULL; 313 return NULL;
264} 314}
265 315
266struct sway_container *output_find_container(struct sway_container *output, 316struct sway_container *output_find_container(struct sway_output *output,
267 bool (*test)(struct sway_container *con, void *data), void *data) { 317 bool (*test)(struct sway_container *con, void *data), void *data) {
268 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
269 return NULL;
270 }
271 struct sway_container *result = NULL; 318 struct sway_container *result = NULL;
272 for (int i = 0; i < output->children->length; ++i) { 319 for (int i = 0; i < output->workspaces->length; ++i) {
273 struct sway_container *workspace = output->children->items[i]; 320 struct sway_workspace *workspace = output->workspaces->items[i];
274 if ((result = workspace_find_container(workspace, test, data))) { 321 if ((result = workspace_find_container(workspace, test, data))) {
275 return result; 322 return result;
276 } 323 }
@@ -279,8 +326,8 @@ struct sway_container *output_find_container(struct sway_container *output,
279} 326}
280 327
281static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { 328static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
282 struct sway_container *a = *(void **)_a; 329 struct sway_workspace *a = *(void **)_a;
283 struct sway_container *b = *(void **)_b; 330 struct sway_workspace *b = *(void **)_b;
284 331
285 if (isdigit(a->name[0]) && isdigit(b->name[0])) { 332 if (isdigit(a->name[0]) && isdigit(b->name[0])) {
286 int a_num = strtol(a->name, NULL, 10); 333 int a_num = strtol(a->name, NULL, 10);
@@ -294,6 +341,27 @@ static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
294 return 0; 341 return 0;
295} 342}
296 343
297void output_sort_workspaces(struct sway_container *output) { 344void output_sort_workspaces(struct sway_output *output) {
298 list_stable_sort(output->children, sort_workspace_cmp_qsort); 345 list_stable_sort(output->workspaces, sort_workspace_cmp_qsort);
346}
347
348void output_get_box(struct sway_output *output, struct wlr_box *box) {
349 box->x = output->wlr_output->lx;
350 box->y = output->wlr_output->ly;
351 box->width = output->wlr_output->width;
352 box->height = output->wlr_output->height;
353}
354
355enum sway_container_layout output_get_default_layout(
356 struct sway_output *output) {
357 if (config->default_layout != L_NONE) {
358 return config->default_layout;
359 }
360 if (config->default_orientation != L_NONE) {
361 return config->default_orientation;
362 }
363 if (output->wlr_output->height > output->wlr_output->width) {
364 return L_VERT;
365 }
366 return L_HORIZ;
299} 367}
diff --git a/sway/tree/root.c b/sway/tree/root.c
index b42371de..ecc04ddb 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -14,54 +14,45 @@
14#include "log.h" 14#include "log.h"
15#include "util.h" 15#include "util.h"
16 16
17struct sway_container root_container; 17struct sway_root *root;
18 18
19static void output_layout_handle_change(struct wl_listener *listener, 19static void output_layout_handle_change(struct wl_listener *listener,
20 void *data) { 20 void *data) {
21 arrange_windows(&root_container); 21 arrange_root();
22 transaction_commit_dirty(); 22 transaction_commit_dirty();
23} 23}
24 24
25void root_create(void) { 25struct sway_root *root_create(void) {
26 root_container.id = 0; // normally assigned in new_swayc() 26 struct sway_root *root = calloc(1, sizeof(struct sway_root));
27 root_container.type = C_ROOT; 27 if (!root) {
28 root_container.layout = L_NONE; 28 wlr_log(WLR_ERROR, "Unable to allocate sway_root");
29 root_container.name = strdup("root"); 29 return NULL;
30 root_container.children = create_list(); 30 }
31 root_container.current.children = create_list(); 31 node_init(&root->node, N_ROOT, root);
32 wl_signal_init(&root_container.events.destroy); 32 root->output_layout = wlr_output_layout_create();
33 33 wl_list_init(&root->all_outputs);
34 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
35 root_container.sway_root->output_layout = wlr_output_layout_create();
36 wl_list_init(&root_container.sway_root->all_outputs);
37#ifdef HAVE_XWAYLAND 34#ifdef HAVE_XWAYLAND
38 wl_list_init(&root_container.sway_root->xwayland_unmanaged); 35 wl_list_init(&root->xwayland_unmanaged);
39#endif 36#endif
40 wl_list_init(&root_container.sway_root->drag_icons); 37 wl_list_init(&root->drag_icons);
41 wl_signal_init(&root_container.sway_root->events.new_container); 38 wl_signal_init(&root->events.new_node);
42 root_container.sway_root->scratchpad = create_list(); 39 root->outputs = create_list();
43 root_container.sway_root->saved_workspaces = create_list(); 40 root->scratchpad = create_list();
44 41 root->saved_workspaces = create_list();
45 root_container.sway_root->output_layout_change.notify = 42
46 output_layout_handle_change; 43 root->output_layout_change.notify = output_layout_handle_change;
47 wl_signal_add(&root_container.sway_root->output_layout->events.change, 44 wl_signal_add(&root->output_layout->events.change,
48 &root_container.sway_root->output_layout_change); 45 &root->output_layout_change);
46 return root;
49} 47}
50 48
51void root_destroy(void) { 49void root_destroy(struct sway_root *root) {
52 // sway_root 50 wl_list_remove(&root->output_layout_change.link);
53 wl_list_remove(&root_container.sway_root->output_layout_change.link); 51 list_free(root->scratchpad);
54 list_free(root_container.sway_root->scratchpad); 52 list_free(root->saved_workspaces);
55 list_free(root_container.sway_root->saved_workspaces); 53 list_free(root->outputs);
56 wlr_output_layout_destroy(root_container.sway_root->output_layout); 54 wlr_output_layout_destroy(root->output_layout);
57 free(root_container.sway_root); 55 free(root);
58
59 // root_container
60 list_free(root_container.children);
61 list_free(root_container.current.children);
62 free(root_container.name);
63
64 memset(&root_container, 0, sizeof(root_container));
65} 56}
66 57
67void root_scratchpad_add_container(struct sway_container *con) { 58void root_scratchpad_add_container(struct sway_container *con) {
@@ -69,15 +60,21 @@ void root_scratchpad_add_container(struct sway_container *con) {
69 return; 60 return;
70 } 61 }
71 con->scratchpad = true; 62 con->scratchpad = true;
72 list_add(root_container.sway_root->scratchpad, con); 63 list_add(root->scratchpad, con);
73 64
74 struct sway_container *parent = con->parent; 65 struct sway_container *parent = con->parent;
66 struct sway_workspace *workspace = con->workspace;
75 container_set_floating(con, true); 67 container_set_floating(con, true);
76 container_remove_child(con); 68 container_detach(con);
77 arrange_windows(parent);
78 69
79 struct sway_seat *seat = input_manager_current_seat(input_manager); 70 struct sway_seat *seat = input_manager_current_seat(input_manager);
80 seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); 71 if (parent) {
72 arrange_container(parent);
73 seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node));
74 } else {
75 arrange_workspace(workspace);
76 seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node));
77 }
81} 78}
82 79
83void root_scratchpad_remove_container(struct sway_container *con) { 80void root_scratchpad_remove_container(struct sway_container *con) {
@@ -85,28 +82,25 @@ void root_scratchpad_remove_container(struct sway_container *con) {
85 return; 82 return;
86 } 83 }
87 con->scratchpad = false; 84 con->scratchpad = false;
88 int index = list_find(root_container.sway_root->scratchpad, con); 85 int index = list_find(root->scratchpad, con);
89 if (index != -1) { 86 if (index != -1) {
90 list_del(root_container.sway_root->scratchpad, index); 87 list_del(root->scratchpad, index);
91 } 88 }
92} 89}
93 90
94void root_scratchpad_show(struct sway_container *con) { 91void root_scratchpad_show(struct sway_container *con) {
95 struct sway_seat *seat = input_manager_current_seat(input_manager); 92 struct sway_seat *seat = input_manager_current_seat(input_manager);
96 struct sway_container *ws = seat_get_focus(seat); 93 struct sway_workspace *ws = seat_get_focused_workspace(seat);
97 if (ws->type != C_WORKSPACE) {
98 ws = container_parent(ws, C_WORKSPACE);
99 }
100 94
101 // If the current con or any of its parents are in fullscreen mode, we 95 // If the current con or any of its parents are in fullscreen mode, we
102 // first need to disable it before showing the scratchpad con. 96 // first need to disable it before showing the scratchpad con.
103 if (ws->sway_workspace->fullscreen) { 97 if (ws->fullscreen) {
104 container_set_fullscreen(ws->sway_workspace->fullscreen, false); 98 container_set_fullscreen(ws->fullscreen, false);
105 } 99 }
106 100
107 // Show the container 101 // Show the container
108 if (con->parent) { 102 if (con->workspace) {
109 container_remove_child(con); 103 container_detach(con);
110 } 104 }
111 workspace_add_floating(ws, con); 105 workspace_add_floating(ws, con);
112 106
@@ -115,7 +109,7 @@ void root_scratchpad_show(struct sway_container *con) {
115 double center_ly = con->y + con->height / 2; 109 double center_ly = con->y + con->height / 2;
116 110
117 struct wlr_box workspace_box; 111 struct wlr_box workspace_box;
118 container_get_box(ws, &workspace_box); 112 workspace_get_box(ws, &workspace_box);
119 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { 113 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
120 // Maybe resize it 114 // Maybe resize it
121 if (con->width > ws->width || con->height > ws->height) { 115 if (con->width > ws->width || con->height > ws->height) {
@@ -128,23 +122,21 @@ void root_scratchpad_show(struct sway_container *con) {
128 container_floating_move_to(con, new_lx, new_ly); 122 container_floating_move_to(con, new_lx, new_ly);
129 } 123 }
130 124
131 arrange_windows(ws); 125 arrange_workspace(ws);
132 seat_set_focus(seat, seat_get_focus_inactive(seat, con)); 126 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
133
134 container_set_dirty(con->parent);
135} 127}
136 128
137void root_scratchpad_hide(struct sway_container *con) { 129void root_scratchpad_hide(struct sway_container *con) {
138 struct sway_seat *seat = input_manager_current_seat(input_manager); 130 struct sway_seat *seat = input_manager_current_seat(input_manager);
139 struct sway_container *focus = seat_get_focus(seat); 131 struct sway_node *focus = seat_get_focus(seat);
140 struct sway_container *ws = container_parent(con, C_WORKSPACE); 132 struct sway_workspace *ws = con->workspace;
141 133
142 container_remove_child(con); 134 container_detach(con);
143 arrange_windows(ws); 135 arrange_workspace(ws);
144 if (con == focus) { 136 if (&con->node == focus) {
145 seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); 137 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
146 } 138 }
147 list_move_to_end(root_container.sway_root->scratchpad, con); 139 list_move_to_end(root->scratchpad, con);
148} 140}
149 141
150struct pid_workspace { 142struct pid_workspace {
@@ -152,7 +144,7 @@ struct pid_workspace {
152 char *workspace; 144 char *workspace;
153 struct timespec time_added; 145 struct timespec time_added;
154 146
155 struct sway_container *output; 147 struct sway_output *output;
156 struct wl_listener output_destroy; 148 struct wl_listener output_destroy;
157 149
158 struct wl_list link; 150 struct wl_list link;
@@ -160,13 +152,13 @@ struct pid_workspace {
160 152
161static struct wl_list pid_workspaces; 153static struct wl_list pid_workspaces;
162 154
163struct sway_container *root_workspace_for_pid(pid_t pid) { 155struct sway_workspace *root_workspace_for_pid(pid_t pid) {
164 if (!pid_workspaces.prev && !pid_workspaces.next) { 156 if (!pid_workspaces.prev && !pid_workspaces.next) {
165 wl_list_init(&pid_workspaces); 157 wl_list_init(&pid_workspaces);
166 return NULL; 158 return NULL;
167 } 159 }
168 160
169 struct sway_container *ws = NULL; 161 struct sway_workspace *ws = NULL;
170 struct pid_workspace *pw = NULL; 162 struct pid_workspace *pw = NULL;
171 163
172 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); 164 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid);
@@ -219,16 +211,12 @@ void root_record_workspace_pid(pid_t pid) {
219 } 211 }
220 212
221 struct sway_seat *seat = input_manager_current_seat(input_manager); 213 struct sway_seat *seat = input_manager_current_seat(input_manager);
222 struct sway_container *ws = 214 struct sway_workspace *ws = seat_get_focused_workspace(seat);
223 seat_get_focus_inactive(seat, &root_container);
224 if (ws && ws->type != C_WORKSPACE) {
225 ws = container_parent(ws, C_WORKSPACE);
226 }
227 if (!ws) { 215 if (!ws) {
228 wlr_log(WLR_DEBUG, "Bailing out, no workspace"); 216 wlr_log(WLR_DEBUG, "Bailing out, no workspace");
229 return; 217 return;
230 } 218 }
231 struct sway_container *output = ws->parent; 219 struct sway_output *output = ws->output;
232 if (!output) { 220 if (!output) {
233 wlr_log(WLR_DEBUG, "Bailing out, no output"); 221 wlr_log(WLR_DEBUG, "Bailing out, no output");
234 return; 222 return;
@@ -255,30 +243,28 @@ void root_record_workspace_pid(pid_t pid) {
255 pw->pid = pid; 243 pw->pid = pid;
256 memcpy(&pw->time_added, &now, sizeof(struct timespec)); 244 memcpy(&pw->time_added, &now, sizeof(struct timespec));
257 pw->output_destroy.notify = pw_handle_output_destroy; 245 pw->output_destroy.notify = pw_handle_output_destroy;
258 wl_signal_add(&output->sway_output->wlr_output->events.destroy, 246 wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
259 &pw->output_destroy);
260 wl_list_insert(&pid_workspaces, &pw->link); 247 wl_list_insert(&pid_workspaces, &pw->link);
261} 248}
262 249
263void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), 250void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
264 void *data) { 251 void *data) {
265 for (int i = 0; i < root_container.children->length; ++i) { 252 for (int i = 0; i < root->outputs->length; ++i) {
266 struct sway_container *output = root_container.children->items[i]; 253 struct sway_output *output = root->outputs->items[i];
267 output_for_each_workspace(output, f, data); 254 output_for_each_workspace(output, f, data);
268 } 255 }
269} 256}
270 257
271void root_for_each_container(void (*f)(struct sway_container *con, void *data), 258void root_for_each_container(void (*f)(struct sway_container *con, void *data),
272 void *data) { 259 void *data) {
273 for (int i = 0; i < root_container.children->length; ++i) { 260 for (int i = 0; i < root->outputs->length; ++i) {
274 struct sway_container *output = root_container.children->items[i]; 261 struct sway_output *output = root->outputs->items[i];
275 output_for_each_container(output, f, data); 262 output_for_each_container(output, f, data);
276 } 263 }
277 264
278 // Scratchpad 265 // Scratchpad
279 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 266 for (int i = 0; i < root->scratchpad->length; ++i) {
280 struct sway_container *container = 267 struct sway_container *container = root->scratchpad->items[i];
281 root_container.sway_root->scratchpad->items[i];
282 // If the container has a parent then it's visible on a workspace 268 // If the container has a parent then it's visible on a workspace
283 // and will have been iterated in the previous for loop. So we only 269 // and will have been iterated in the previous for loop. So we only
284 // iterate the hidden scratchpad containers here. 270 // iterate the hidden scratchpad containers here.
@@ -289,10 +275,10 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
289 } 275 }
290} 276}
291 277
292struct sway_container *root_find_output( 278struct sway_output *root_find_output(
293 bool (*test)(struct sway_container *con, void *data), void *data) { 279 bool (*test)(struct sway_output *output, void *data), void *data) {
294 for (int i = 0; i < root_container.children->length; ++i) { 280 for (int i = 0; i < root->outputs->length; ++i) {
295 struct sway_container *output = root_container.children->items[i]; 281 struct sway_output *output = root->outputs->items[i];
296 if (test(output, data)) { 282 if (test(output, data)) {
297 return output; 283 return output;
298 } 284 }
@@ -300,11 +286,11 @@ struct sway_container *root_find_output(
300 return NULL; 286 return NULL;
301} 287}
302 288
303struct sway_container *root_find_workspace( 289struct sway_workspace *root_find_workspace(
304 bool (*test)(struct sway_container *con, void *data), void *data) { 290 bool (*test)(struct sway_workspace *ws, void *data), void *data) {
305 struct sway_container *result = NULL; 291 struct sway_workspace *result = NULL;
306 for (int i = 0; i < root_container.children->length; ++i) { 292 for (int i = 0; i < root->outputs->length; ++i) {
307 struct sway_container *output = root_container.children->items[i]; 293 struct sway_output *output = root->outputs->items[i];
308 if ((result = output_find_workspace(output, test, data))) { 294 if ((result = output_find_workspace(output, test, data))) {
309 return result; 295 return result;
310 } 296 }
@@ -315,17 +301,16 @@ struct sway_container *root_find_workspace(
315struct sway_container *root_find_container( 301struct sway_container *root_find_container(
316 bool (*test)(struct sway_container *con, void *data), void *data) { 302 bool (*test)(struct sway_container *con, void *data), void *data) {
317 struct sway_container *result = NULL; 303 struct sway_container *result = NULL;
318 for (int i = 0; i < root_container.children->length; ++i) { 304 for (int i = 0; i < root->outputs->length; ++i) {
319 struct sway_container *output = root_container.children->items[i]; 305 struct sway_output *output = root->outputs->items[i];
320 if ((result = output_find_container(output, test, data))) { 306 if ((result = output_find_container(output, test, data))) {
321 return result; 307 return result;
322 } 308 }
323 } 309 }
324 310
325 // Scratchpad 311 // Scratchpad
326 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 312 for (int i = 0; i < root->scratchpad->length; ++i) {
327 struct sway_container *container = 313 struct sway_container *container = root->scratchpad->items[i];
328 root_container.sway_root->scratchpad->items[i];
329 if (!container->parent) { 314 if (!container->parent) {
330 if (test(container, data)) { 315 if (test(container, data)) {
331 return container; 316 return container;
@@ -337,3 +322,10 @@ struct sway_container *root_find_container(
337 } 322 }
338 return NULL; 323 return NULL;
339} 324}
325
326void root_get_box(struct sway_root *root, struct wlr_box *box) {
327 box->x = root->x;
328 box->y = root->y;
329 box->width = root->width;
330 box->height = root->height;
331}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 6bd0ef67..452c2bd1 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type,
33 view->marks = create_list(); 33 view->marks = create_list();
34 view->allow_request_urgent = true; 34 view->allow_request_urgent = true;
35 wl_signal_init(&view->events.unmap); 35 wl_signal_init(&view->events.unmap);
36
37 view->container = container_create(view);
36} 38}
37 39
38void view_destroy(struct sway_view *view) { 40void view_destroy(struct sway_view *view) {
@@ -43,8 +45,8 @@ void view_destroy(struct sway_view *view) {
43 "Tried to free view which wasn't marked as destroying")) { 45 "Tried to free view which wasn't marked as destroying")) {
44 return; 46 return;
45 } 47 }
46 if (!sway_assert(view->swayc == NULL, 48 if (!sway_assert(view->container == NULL,
47 "Tried to free view which still has a swayc " 49 "Tried to free view which still has a container "
48 "(might have a pending transaction?)")) { 50 "(might have a pending transaction?)")) {
49 return; 51 return;
50 } 52 }
@@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) {
57 wlr_texture_destroy(view->marks_focused_inactive); 59 wlr_texture_destroy(view->marks_focused_inactive);
58 wlr_texture_destroy(view->marks_unfocused); 60 wlr_texture_destroy(view->marks_unfocused);
59 wlr_texture_destroy(view->marks_urgent); 61 wlr_texture_destroy(view->marks_urgent);
62 free(view->title_format);
60 63
61 if (view->impl->destroy) { 64 if (view->impl->destroy) {
62 view->impl->destroy(view); 65 view->impl->destroy(view);
@@ -65,23 +68,13 @@ void view_destroy(struct sway_view *view) {
65 } 68 }
66} 69}
67 70
68/**
69 * The view may or may not be involved in a transaction. For example, a view may
70 * unmap then attempt to destroy itself before we've applied the new layout. If
71 * an unmapping view is still involved in a transaction then it'll still have a
72 * swayc.
73 *
74 * If there's no transaction we can simply free the view. Otherwise the
75 * destroying flag will make the view get freed when the transaction is
76 * finished.
77 */
78void view_begin_destroy(struct sway_view *view) { 71void view_begin_destroy(struct sway_view *view) {
79 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { 72 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
80 return; 73 return;
81 } 74 }
82 view->destroying = true; 75 view->destroying = true;
83 76
84 if (!view->swayc) { 77 if (!view->container) {
85 view_destroy(view); 78 view_destroy(view);
86 } 79 }
87} 80}
@@ -171,30 +164,27 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
171} 164}
172 165
173void view_autoconfigure(struct sway_view *view) { 166void view_autoconfigure(struct sway_view *view) {
174 if (!sway_assert(view->swayc, 167 struct sway_output *output = view->container->workspace->output;
175 "Called view_autoconfigure() on a view without a swayc")) {
176 return;
177 }
178
179 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
180 168
181 if (view->swayc->is_fullscreen) { 169 if (view->container->is_fullscreen) {
182 view->x = output->x; 170 view->x = output->wlr_output->lx;
183 view->y = output->y; 171 view->y = output->wlr_output->ly;
184 view->width = output->width; 172 view->width = output->wlr_output->width;
185 view->height = output->height; 173 view->height = output->wlr_output->height;
186 return; 174 return;
187 } 175 }
188 176
189 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 177 struct sway_workspace *ws = view->container->workspace;
190 178
191 int other_views = 0; 179 bool other_views = false;
192 if (config->hide_edge_borders == E_SMART) { 180 if (config->hide_edge_borders == E_SMART) {
193 struct sway_container *con = view->swayc; 181 struct sway_container *con = view->container;
194 while (con != output) { 182 while (con) {
195 if (con->layout != L_TABBED && con->layout != L_STACKED) { 183 enum sway_container_layout layout = container_parent_layout(con);
196 other_views += con->children ? con->children->length - 1 : 0; 184 if (layout != L_TABBED && layout != L_STACKED) {
197 if (other_views > 0) { 185 list_t *siblings = container_get_siblings(con);
186 if (siblings && siblings->length > 1) {
187 other_views = true;
198 break; 188 break;
199 } 189 }
200 } 190 }
@@ -202,7 +192,7 @@ void view_autoconfigure(struct sway_view *view) {
202 } 192 }
203 } 193 }
204 194
205 struct sway_container *con = view->swayc; 195 struct sway_container *con = view->container;
206 196
207 view->border_top = view->border_bottom = true; 197 view->border_top = view->border_bottom = true;
208 view->border_left = view->border_right = true; 198 view->border_left = view->border_right = true;
@@ -228,7 +218,8 @@ void view_autoconfigure(struct sway_view *view) {
228 // In a tabbed or stacked container, the swayc's y is the bottom of the 218 // In a tabbed or stacked container, the swayc's y is the bottom of the
229 // title area. We have to disable any top border because the title bar is 219 // title area. We have to disable any top border because the title bar is
230 // rendered by the parent. 220 // rendered by the parent.
231 if (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED) { 221 enum sway_container_layout layout = container_parent_layout(con);
222 if (layout == L_TABBED || layout == L_STACKED) {
232 view->border_top = false; 223 view->border_top = false;
233 } else { 224 } else {
234 y_offset = container_titlebar_height(); 225 y_offset = container_titlebar_height();
@@ -281,13 +272,16 @@ void view_set_activated(struct sway_view *view, bool activated) {
281} 272}
282 273
283void view_request_activate(struct sway_view *view) { 274void view_request_activate(struct sway_view *view) {
284 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 275 struct sway_workspace *ws = view->container->workspace;
276 if (!ws) { // hidden scratchpad container
277 return;
278 }
285 struct sway_seat *seat = input_manager_current_seat(input_manager); 279 struct sway_seat *seat = input_manager_current_seat(input_manager);
286 280
287 switch (config->focus_on_window_activation) { 281 switch (config->focus_on_window_activation) {
288 case FOWA_SMART: 282 case FOWA_SMART:
289 if (workspace_is_visible(ws)) { 283 if (workspace_is_visible(ws)) {
290 seat_set_focus(seat, view->swayc); 284 seat_set_focus(seat, &view->container->node);
291 } else { 285 } else {
292 view_set_urgent(view, true); 286 view_set_urgent(view, true);
293 } 287 }
@@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) {
296 view_set_urgent(view, true); 290 view_set_urgent(view, true);
297 break; 291 break;
298 case FOWA_FOCUS: 292 case FOWA_FOCUS:
299 seat_set_focus(seat, view->swayc); 293 seat_set_focus(seat, &view->container->node);
300 break; 294 break;
301 case FOWA_NONE: 295 case FOWA_NONE:
302 break; 296 break;
@@ -331,23 +325,12 @@ void view_close_popups(struct sway_view *view) {
331} 325}
332 326
333void view_damage_from(struct sway_view *view) { 327void view_damage_from(struct sway_view *view) {
334 for (int i = 0; i < root_container.children->length; ++i) { 328 for (int i = 0; i < root->outputs->length; ++i) {
335 struct sway_container *cont = root_container.children->items[i]; 329 struct sway_output *output = root->outputs->items[i];
336 if (cont->type == C_OUTPUT) { 330 output_damage_from_view(output, view);
337 output_damage_from_view(cont->sway_output, view);
338 }
339 } 331 }
340} 332}
341 333
342static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
343 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
344
345 box->x = output->x + view->swayc->x;
346 box->y = output->y + view->swayc->y;
347 box->width = view->width;
348 box->height = view->height;
349}
350
351void view_for_each_surface(struct sway_view *view, 334void view_for_each_surface(struct sway_view *view,
352 wlr_surface_iterator_func_t iterator, void *user_data) { 335 wlr_surface_iterator_func_t iterator, void *user_data) {
353 if (!view->surface) { 336 if (!view->surface) {
@@ -396,11 +379,8 @@ static bool view_has_executed_criteria(struct sway_view *view,
396} 379}
397 380
398void view_execute_criteria(struct sway_view *view) { 381void view_execute_criteria(struct sway_view *view) {
399 if (!view->swayc) {
400 return;
401 }
402 struct sway_seat *seat = input_manager_current_seat(input_manager); 382 struct sway_seat *seat = input_manager_current_seat(input_manager);
403 struct sway_container *prior_focus = seat_get_focus(seat); 383 struct sway_node *prior_focus = seat_get_focus(seat);
404 list_t *criterias = criteria_for_view(view, CT_COMMAND); 384 list_t *criterias = criteria_for_view(view, CT_COMMAND);
405 for (int i = 0; i < criterias->length; i++) { 385 for (int i = 0; i < criterias->length; i++) {
406 struct criteria *criteria = criterias->items[i]; 386 struct criteria *criteria = criterias->items[i];
@@ -411,7 +391,7 @@ void view_execute_criteria(struct sway_view *view) {
411 } 391 }
412 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 392 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
413 criteria->raw, view, criteria->cmdlist); 393 criteria->raw, view, criteria->cmdlist);
414 seat_set_focus(seat, view->swayc); 394 seat_set_focus(seat, &view->container->node);
415 list_add(view->executed_criteria, criteria); 395 list_add(view->executed_criteria, criteria);
416 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 396 struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
417 if (res->status != CMD_SUCCESS) { 397 if (res->status != CMD_SUCCESS) {
@@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) {
423 seat_set_focus(seat, prior_focus); 403 seat_set_focus(seat, prior_focus);
424} 404}
425 405
426static struct sway_container *select_workspace(struct sway_view *view) { 406static struct sway_workspace *select_workspace(struct sway_view *view) {
427 struct sway_seat *seat = input_manager_current_seat(input_manager); 407 struct sway_seat *seat = input_manager_current_seat(input_manager);
428 408
429 // Check if there's any `assign` criteria for the view 409 // Check if there's any `assign` criteria for the view
430 list_t *criterias = criteria_for_view(view, 410 list_t *criterias = criteria_for_view(view,
431 CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); 411 CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT);
432 struct sway_container *ws = NULL; 412 struct sway_workspace *ws = NULL;
433 for (int i = 0; i < criterias->length; ++i) { 413 for (int i = 0; i < criterias->length; ++i) {
434 struct criteria *criteria = criterias->items[i]; 414 struct criteria *criteria = criterias->items[i];
435 if (criteria->type == CT_ASSIGN_OUTPUT) { 415 if (criteria->type == CT_ASSIGN_OUTPUT) {
436 struct sway_container *output = output_by_name(criteria->target); 416 struct sway_output *output = output_by_name(criteria->target);
437 if (output) { 417 if (output) {
438 ws = seat_get_active_child(seat, output); 418 ws = output_get_active_workspace(output);
439 break; 419 break;
440 } 420 }
441 } else { 421 } else {
@@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) {
484 } 464 }
485 465
486 // Use the focused workspace 466 // Use the focused workspace
487 ws = seat_get_focus_inactive(seat, &root_container); 467 return seat_get_focused_workspace(seat);
488 if (ws->type != C_WORKSPACE) {
489 ws = container_parent(ws, C_WORKSPACE);
490 }
491 return ws;
492} 468}
493 469
494static bool should_focus(struct sway_view *view) { 470static bool should_focus(struct sway_view *view) {
495 struct sway_seat *seat = input_manager_current_seat(input_manager); 471 struct sway_seat *seat = input_manager_current_seat(input_manager);
496 struct sway_container *prev_focus = 472 struct sway_container *prev_con = seat_get_focused_container(seat);
497 seat_get_focus_inactive(seat, &root_container); 473 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
498 struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? 474 struct sway_workspace *map_ws = view->container->workspace;
499 prev_focus : container_parent(prev_focus, C_WORKSPACE);
500 struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE);
501 475
502 // Views can only take focus if they are mapped into the active workspace 476 // Views can only take focus if they are mapped into the active workspace
503 if (prev_ws != map_ws) { 477 if (prev_ws != map_ws) {
@@ -506,10 +480,9 @@ static bool should_focus(struct sway_view *view) {
506 480
507 // If the view is the only one in the focused workspace, it'll get focus 481 // If the view is the only one in the focused workspace, it'll get focus
508 // regardless of any no_focus criteria. 482 // regardless of any no_focus criteria.
509 struct sway_container *parent = view->swayc->parent; 483 if (!view->container->parent && !prev_con) {
510 if (parent->type == C_WORKSPACE && prev_focus == parent) { 484 size_t num_children = view->container->workspace->tiling->length +
511 size_t num_children = parent->children->length + 485 view->container->workspace->floating->length;
512 parent->sway_workspace->floating->length;
513 if (num_children == 1) { 486 if (num_children == 1) {
514 return true; 487 return true;
515 } 488 }
@@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
529 view->surface = wlr_surface; 502 view->surface = wlr_surface;
530 503
531 struct sway_seat *seat = input_manager_current_seat(input_manager); 504 struct sway_seat *seat = input_manager_current_seat(input_manager);
532 struct sway_container *ws = select_workspace(view); 505 struct sway_workspace *ws = select_workspace(view);
533 struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); 506 struct sway_node *node = seat_get_focus_inactive(seat, &ws->node);
507 struct sway_container *target_sibling = node->type == N_CONTAINER ?
508 node->sway_container : NULL;
534 509
535 // If we're about to launch the view into the floating container, then 510 // If we're about to launch the view into the floating container, then
536 // launch it as a tiled view in the root of the workspace instead. 511 // launch it as a tiled view in the root of the workspace instead.
537 if (container_is_floating(target_sibling)) { 512 if (target_sibling && container_is_floating(target_sibling)) {
538 target_sibling = target_sibling->parent; 513 target_sibling = NULL;
539 } 514 }
540 515
541 view->swayc = container_view_create(target_sibling, view); 516 view->container = container_create(view);
517 if (target_sibling) {
518 container_add_sibling(target_sibling, view->container, 1);
519 } else {
520 workspace_add_tiling(ws, view->container);
521 }
522 ipc_event_window(view->container, "new");
542 523
543 view_init_subsurfaces(view, wlr_surface); 524 view_init_subsurfaces(view, wlr_surface);
544 wl_signal_add(&wlr_surface->events.new_subsurface, 525 wl_signal_add(&wlr_surface->events.new_subsurface,
@@ -548,7 +529,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
548 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 529 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
549 view->border = config->floating_border; 530 view->border = config->floating_border;
550 view->border_thickness = config->floating_border_thickness; 531 view->border_thickness = config->floating_border_thickness;
551 container_set_floating(view->swayc, true); 532 container_set_floating(view->container, true);
552 } else { 533 } else {
553 view->border = config->border; 534 view->border = config->border;
554 view->border_thickness = config->border_thickness; 535 view->border_thickness = config->border_thickness;
@@ -556,11 +537,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
556 } 537 }
557 538
558 if (should_focus(view)) { 539 if (should_focus(view)) {
559 input_manager_set_focus(input_manager, view->swayc); 540 input_manager_set_focus(input_manager, &view->container->node);
560 } 541 }
561 542
562 view_update_title(view, false); 543 view_update_title(view, false);
563 container_notify_subtree_changed(view->swayc->parent); 544 container_update_representation(view->container);
564 view_execute_criteria(view); 545 view_execute_criteria(view);
565} 546}
566 547
@@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) {
574 view->urgent_timer = NULL; 555 view->urgent_timer = NULL;
575 } 556 }
576 557
577 bool was_fullscreen = view->swayc->is_fullscreen; 558 struct sway_container *parent = view->container->parent;
578 struct sway_container *parent = view->swayc->parent; 559 struct sway_workspace *ws = view->container->workspace;
579 container_begin_destroy(view->swayc); 560 container_begin_destroy(view->container);
580 struct sway_container *surviving_ancestor = container_reap_empty(parent); 561 if (parent) {
562 container_reap_empty(parent);
563 } else {
564 workspace_consider_destroy(ws);
565 }
581 566
582 // If the workspace wasn't reaped 567 if (!ws->node.destroying) {
583 if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { 568 arrange_workspace(ws);
584 struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ?
585 surviving_ancestor :
586 container_parent(surviving_ancestor, C_WORKSPACE);
587 arrange_windows(was_fullscreen ? ws : surviving_ancestor);
588 workspace_detect_urgent(ws); 569 workspace_detect_urgent(ws);
589 } 570 }
590 571
@@ -593,15 +574,15 @@ void view_unmap(struct sway_view *view) {
593} 574}
594 575
595void view_update_size(struct sway_view *view, int width, int height) { 576void view_update_size(struct sway_view *view, int width, int height) {
596 if (!sway_assert(container_is_floating(view->swayc), 577 if (!sway_assert(container_is_floating(view->container),
597 "Expected a floating container")) { 578 "Expected a floating container")) {
598 return; 579 return;
599 } 580 }
600 view->width = width; 581 view->width = width;
601 view->height = height; 582 view->height = height;
602 view->swayc->current.view_width = width; 583 view->container->current.view_width = width;
603 view->swayc->current.view_height = height; 584 view->container->current.view_height = height;
604 container_set_geometry_from_floating_view(view->swayc); 585 container_set_geometry_from_floating_view(view->container);
605} 586}
606 587
607static void view_subsurface_create(struct sway_view *view, 588static void view_subsurface_create(struct sway_view *view,
@@ -670,27 +651,18 @@ void view_child_init(struct sway_view_child *child,
670 wl_signal_add(&view->events.unmap, &child->view_unmap); 651 wl_signal_add(&view->events.unmap, &child->view_unmap);
671 child->view_unmap.notify = view_child_handle_view_unmap; 652 child->view_unmap.notify = view_child_handle_view_unmap;
672 653
673 struct sway_container *output = child->view->swayc->parent; 654 struct sway_output *output = child->view->container->workspace->output;
674 if (output != NULL) { 655 wlr_surface_send_enter(child->surface, output->wlr_output);
675 if (output->type != C_OUTPUT) {
676 output = container_parent(output, C_OUTPUT);
677 }
678 wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
679 }
680 656
681 view_init_subsurfaces(child->view, surface); 657 view_init_subsurfaces(child->view, surface);
682 658
683 // TODO: only damage the whole child 659 // TODO: only damage the whole child
684 if (child->view->swayc) { 660 container_damage_whole(child->view->container);
685 container_damage_whole(child->view->swayc);
686 }
687} 661}
688 662
689void view_child_destroy(struct sway_view_child *child) { 663void view_child_destroy(struct sway_view_child *child) {
690 // TODO: only damage the whole child 664 // TODO: only damage the whole child
691 if (child->view->swayc) { 665 container_damage_whole(child->view->container);
692 container_damage_whole(child->view->swayc);
693 }
694 666
695 wl_list_remove(&child->surface_commit.link); 667 wl_list_remove(&child->surface_commit.link);
696 wl_list_remove(&child->surface_destroy.link); 668 wl_list_remove(&child->surface_destroy.link);
@@ -808,22 +780,20 @@ static char *escape_title(char *buffer) {
808} 780}
809 781
810void view_update_title(struct sway_view *view, bool force) { 782void view_update_title(struct sway_view *view, bool force) {
811 if (!view->swayc) {
812 return;
813 }
814 const char *title = view_get_title(view); 783 const char *title = view_get_title(view);
815 784
816 if (!force) { 785 if (!force) {
817 if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { 786 if (title && view->container->title &&
787 strcmp(title, view->container->title) == 0) {
818 return; 788 return;
819 } 789 }
820 if (!title && !view->swayc->name) { 790 if (!title && !view->container->title) {
821 return; 791 return;
822 } 792 }
823 } 793 }
824 794
825 free(view->swayc->name); 795 free(view->container->title);
826 free(view->swayc->formatted_title); 796 free(view->container->formatted_title);
827 if (title) { 797 if (title) {
828 size_t len = parse_title_format(view, NULL); 798 size_t len = parse_title_format(view, NULL);
829 char *buffer = calloc(len + 1, sizeof(char)); 799 char *buffer = calloc(len + 1, sizeof(char));
@@ -836,25 +806,25 @@ void view_update_title(struct sway_view *view, bool force) {
836 buffer = escape_title(buffer); 806 buffer = escape_title(buffer);
837 } 807 }
838 808
839 view->swayc->name = strdup(title); 809 view->container->title = strdup(title);
840 view->swayc->formatted_title = buffer; 810 view->container->formatted_title = buffer;
841 } else { 811 } else {
842 view->swayc->name = NULL; 812 view->container->title = NULL;
843 view->swayc->formatted_title = NULL; 813 view->container->formatted_title = NULL;
844 } 814 }
845 container_calculate_title_height(view->swayc); 815 container_calculate_title_height(view->container);
846 config_update_font_height(false); 816 config_update_font_height(false);
847 817
848 // Update title after the global font height is updated 818 // Update title after the global font height is updated
849 container_update_title_textures(view->swayc); 819 container_update_title_textures(view->container);
850 820
851 ipc_event_window(view->swayc, "title"); 821 ipc_event_window(view->container, "title");
852} 822}
853 823
854static bool find_by_mark_iterator(struct sway_container *con, 824static bool find_by_mark_iterator(struct sway_container *con,
855 void *data) { 825 void *data) {
856 char *mark = data; 826 char *mark = data;
857 return con->type == C_VIEW && view_has_mark(con->sway_view, mark); 827 return con->view && view_has_mark(con->view, mark);
858} 828}
859 829
860struct sway_view *view_find_mark(char *mark) { 830struct sway_view *view_find_mark(char *mark) {
@@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) {
863 if (!container) { 833 if (!container) {
864 return NULL; 834 return NULL;
865 } 835 }
866 return container->sway_view; 836 return container->view;
867} 837}
868 838
869bool view_find_and_unmark(char *mark) { 839bool view_find_and_unmark(char *mark) {
@@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) {
872 if (!container) { 842 if (!container) {
873 return false; 843 return false;
874 } 844 }
875 struct sway_view *view = container->sway_view; 845 struct sway_view *view = container->view;
876 846
877 for (int i = 0; i < view->marks->length; ++i) { 847 for (int i = 0; i < view->marks->length; ++i) {
878 char *view_mark = view->marks->items[i]; 848 char *view_mark = view->marks->items[i];
@@ -888,10 +858,9 @@ bool view_find_and_unmark(char *mark) {
888} 858}
889 859
890void view_clear_marks(struct sway_view *view) { 860void view_clear_marks(struct sway_view *view) {
891 while (view->marks->length) { 861 list_foreach(view->marks, free);
892 list_del(view->marks, 0); 862 view->marks->length = 0;
893 ipc_event_window(view->swayc, "mark"); 863 ipc_event_window(view->container, "mark");
894 }
895} 864}
896 865
897bool view_has_mark(struct sway_view *view, char *mark) { 866bool view_has_mark(struct sway_view *view, char *mark) {
@@ -906,12 +875,13 @@ bool view_has_mark(struct sway_view *view, char *mark) {
906 875
907void view_add_mark(struct sway_view *view, char *mark) { 876void view_add_mark(struct sway_view *view, char *mark) {
908 list_add(view->marks, strdup(mark)); 877 list_add(view->marks, strdup(mark));
909 ipc_event_window(view->swayc, "mark"); 878 ipc_event_window(view->container, "mark");
910} 879}
911 880
912static void update_marks_texture(struct sway_view *view, 881static void update_marks_texture(struct sway_view *view,
913 struct wlr_texture **texture, struct border_colors *class) { 882 struct wlr_texture **texture, struct border_colors *class) {
914 struct sway_output *output = container_get_effective_output(view->swayc); 883 struct sway_output *output =
884 container_get_effective_output(view->container);
915 if (!output) { 885 if (!output) {
916 return; 886 return;
917 } 887 }
@@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view,
949 919
950 double scale = output->wlr_output->scale; 920 double scale = output->wlr_output->scale;
951 int width = 0; 921 int width = 0;
952 int height = view->swayc->title_height * scale; 922 int height = view->container->title_height * scale;
953 923
954 cairo_t *c = cairo_create(NULL); 924 cairo_t *c = cairo_create(NULL);
955 get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); 925 get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer);
@@ -994,44 +964,40 @@ void view_update_marks_textures(struct sway_view *view) {
994 &config->border_colors.unfocused); 964 &config->border_colors.unfocused);
995 update_marks_texture(view, &view->marks_urgent, 965 update_marks_texture(view, &view->marks_urgent,
996 &config->border_colors.urgent); 966 &config->border_colors.urgent);
997 container_damage_whole(view->swayc); 967 container_damage_whole(view->container);
998} 968}
999 969
1000bool view_is_visible(struct sway_view *view) { 970bool view_is_visible(struct sway_view *view) {
1001 if (!view->swayc || view->swayc->destroying) { 971 if (view->container->node.destroying) {
1002 return false; 972 return false;
1003 } 973 }
1004 struct sway_container *workspace = 974 struct sway_workspace *workspace = view->container->workspace;
1005 container_parent(view->swayc, C_WORKSPACE);
1006 if (!workspace) { 975 if (!workspace) {
1007 return false; 976 return false;
1008 } 977 }
1009 // Determine if view is nested inside a floating container which is sticky. 978 // Determine if view is nested inside a floating container which is sticky
1010 // A simple floating view will have this ancestry: 979 struct sway_container *floater = view->container;
1011 // C_VIEW -> floating -> workspace 980 while (floater->parent) {
1012 // A more complex ancestry could be:
1013 // C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace
1014 struct sway_container *floater = view->swayc;
1015 while (floater->parent->type != C_WORKSPACE
1016 && floater->parent->parent->type != C_WORKSPACE) {
1017 floater = floater->parent; 981 floater = floater->parent;
1018 } 982 }
1019 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 983 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
1020 // Check view isn't in a tabbed or stacked container on an inactive tab 984 // Check view isn't in a tabbed or stacked container on an inactive tab
1021 struct sway_seat *seat = input_manager_current_seat(input_manager); 985 struct sway_seat *seat = input_manager_current_seat(input_manager);
1022 struct sway_container *container = view->swayc; 986 struct sway_container *container = view->container;
1023 while (container->type != C_WORKSPACE) { 987 while (container) {
1024 if (container->parent->layout == L_TABBED || 988 enum sway_container_layout layout = container_parent_layout(container);
1025 container->parent->layout == L_STACKED) { 989 if (layout == L_TABBED || layout == L_STACKED) {
1026 if (seat_get_active_child(seat, container->parent) != container) { 990 struct sway_node *parent = container->parent ?
991 &container->parent->node : &container->workspace->node;
992 if (seat_get_active_child(seat, parent) != &container->node) {
1027 return false; 993 return false;
1028 } 994 }
1029 } 995 }
1030 container = container->parent; 996 container = container->parent;
1031 } 997 }
1032 // Check view isn't hidden by another fullscreen view 998 // Check view isn't hidden by another fullscreen view
1033 if (workspace->sway_workspace->fullscreen && 999 if (workspace->fullscreen &&
1034 !container_is_fullscreen_or_child(view->swayc)) { 1000 !container_is_fullscreen_or_child(view->container)) {
1035 return false; 1001 return false;
1036 } 1002 }
1037 // Check the workspace is visible 1003 // Check the workspace is visible
@@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1047 } 1013 }
1048 if (enable) { 1014 if (enable) {
1049 struct sway_seat *seat = input_manager_current_seat(input_manager); 1015 struct sway_seat *seat = input_manager_current_seat(input_manager);
1050 if (seat_get_focus(seat) == view->swayc) { 1016 if (seat_get_focused_container(seat) == view->container) {
1051 return; 1017 return;
1052 } 1018 }
1053 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1019 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
@@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1058 view->urgent_timer = NULL; 1024 view->urgent_timer = NULL;
1059 } 1025 }
1060 } 1026 }
1061 container_damage_whole(view->swayc); 1027 container_damage_whole(view->container);
1062 1028
1063 ipc_event_window(view->swayc, "urgent"); 1029 ipc_event_window(view->container, "urgent");
1064 1030
1065 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 1031 workspace_detect_urgent(view->container->workspace);
1066 workspace_detect_urgent(ws);
1067} 1032}
1068 1033
1069bool view_is_urgent(struct sway_view *view) { 1034bool view_is_urgent(struct sway_view *view) {
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 1957d94f..38ee478e 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -12,128 +12,105 @@
12#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/tree/arrange.h" 13#include "sway/tree/arrange.h"
14#include "sway/tree/container.h" 14#include "sway/tree/container.h"
15#include "sway/tree/node.h"
15#include "sway/tree/view.h" 16#include "sway/tree/view.h"
16#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
17#include "list.h" 18#include "list.h"
18#include "log.h" 19#include "log.h"
19#include "util.h" 20#include "util.h"
20 21
21struct sway_container *workspace_get_initial_output(const char *name) { 22struct sway_output *workspace_get_initial_output(const char *name) {
22 struct sway_container *parent;
23 // Search for workspace<->output pair 23 // Search for workspace<->output pair
24 int e = config->workspace_outputs->length;
25 for (int i = 0; i < config->workspace_outputs->length; ++i) { 24 for (int i = 0; i < config->workspace_outputs->length; ++i) {
26 struct workspace_output *wso = config->workspace_outputs->items[i]; 25 struct workspace_output *wso = config->workspace_outputs->items[i];
27 if (strcasecmp(wso->workspace, name) == 0) { 26 if (strcasecmp(wso->workspace, name) == 0) {
28 // Find output to use if it exists 27 // Find output to use if it exists
29 e = root_container.children->length; 28 struct sway_output *output = output_by_name(wso->output);
30 for (i = 0; i < e; ++i) { 29 if (output) {
31 parent = root_container.children->items[i]; 30 return output;
32 if (strcmp(parent->name, wso->output) == 0) {
33 return parent;
34 }
35 } 31 }
36 break; 32 break;
37 } 33 }
38 } 34 }
39 // Otherwise put it on the focused output 35 // Otherwise put it on the focused output
40 struct sway_seat *seat = input_manager_current_seat(input_manager); 36 struct sway_seat *seat = input_manager_current_seat(input_manager);
41 struct sway_container *focus = 37 struct sway_workspace *focus = seat_get_focused_workspace(seat);
42 seat_get_focus_inactive(seat, &root_container); 38 return focus->output;
43 parent = focus;
44 parent = container_parent(parent, C_OUTPUT);
45 return parent;
46} 39}
47 40
48struct sway_container *workspace_create(struct sway_container *output, 41struct sway_workspace *workspace_create(struct sway_output *output,
49 const char *name) { 42 const char *name) {
50 if (output == NULL) { 43 if (output == NULL) {
51 output = workspace_get_initial_output(name); 44 output = workspace_get_initial_output(name);
52 } 45 }
53 46
54 wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); 47 wlr_log(WLR_DEBUG, "Adding workspace %s for output %s", name,
55 struct sway_container *workspace = container_create(C_WORKSPACE); 48 output->wlr_output->name);
56
57 workspace->x = output->x;
58 workspace->y = output->y;
59 workspace->width = output->width;
60 workspace->height = output->height;
61 workspace->name = !name ? NULL : strdup(name);
62 workspace->prev_split_layout = L_NONE;
63 workspace->layout = container_get_default_layout(output);
64 49
65 struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); 50 struct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace));
66 if (!swayws) { 51 if (!ws) {
52 wlr_log(WLR_ERROR, "Unable to allocate sway_workspace");
67 return NULL; 53 return NULL;
68 } 54 }
69 swayws->swayc = workspace; 55 node_init(&ws->node, N_WORKSPACE, ws);
70 swayws->floating = create_list(); 56 ws->x = output->wlr_output->lx;
71 swayws->output_priority = create_list(); 57 ws->y = output->wlr_output->ly;
72 workspace->sway_workspace = swayws; 58 ws->width = output->wlr_output->width;
73 workspace_output_add_priority(workspace, output); 59 ws->height = output->wlr_output->height;
74 60 ws->name = name ? strdup(name) : NULL;
75 container_add_child(output, workspace); 61 ws->prev_split_layout = L_NONE;
62 ws->layout = output_get_default_layout(output);
63 ws->floating = create_list();
64 ws->tiling = create_list();
65 ws->output_priority = create_list();
66 workspace_output_add_priority(ws, output);
67
68 output_add_workspace(output, ws);
76 output_sort_workspaces(output); 69 output_sort_workspaces(output);
77 container_create_notify(workspace);
78 70
79 return workspace; 71 ipc_event_workspace(NULL, ws, "init");
72 wl_signal_emit(&root->events.new_node, &ws->node);
73
74 return ws;
80} 75}
81 76
82void workspace_destroy(struct sway_container *workspace) { 77void workspace_destroy(struct sway_workspace *workspace) {
83 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 78 if (!sway_assert(workspace->node.destroying,
84 return;
85 }
86 if (!sway_assert(workspace->destroying,
87 "Tried to free workspace which wasn't marked as destroying")) { 79 "Tried to free workspace which wasn't marked as destroying")) {
88 return; 80 return;
89 } 81 }
90 if (!sway_assert(workspace->ntxnrefs == 0, "Tried to free workspace " 82 if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace "
91 "which is still referenced by transactions")) { 83 "which is still referenced by transactions")) {
92 return; 84 return;
93 } 85 }
94 // sway_workspace
95 struct sway_workspace *ws = workspace->sway_workspace;
96 list_foreach(ws->output_priority, free);
97 list_free(ws->output_priority);
98 list_free(ws->floating);
99 free(ws);
100 86
101 // swayc
102 free(workspace->name); 87 free(workspace->name);
103 free(workspace->formatted_title); 88 free(workspace->representation);
104 wlr_texture_destroy(workspace->title_focused); 89 list_foreach(workspace->output_priority, free);
105 wlr_texture_destroy(workspace->title_focused_inactive); 90 list_free(workspace->output_priority);
106 wlr_texture_destroy(workspace->title_unfocused); 91 list_free(workspace->floating);
107 wlr_texture_destroy(workspace->title_urgent); 92 list_free(workspace->tiling);
108 list_free(workspace->children); 93 list_free(workspace->current.floating);
109 list_free(workspace->current.children); 94 list_free(workspace->current.tiling);
110 list_free(workspace->outputs);
111 free(workspace); 95 free(workspace);
112} 96}
113 97
114void workspace_begin_destroy(struct sway_container *workspace) { 98void workspace_begin_destroy(struct sway_workspace *workspace) {
115 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
116 return;
117 }
118 wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); 99 wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name);
119 wl_signal_emit(&workspace->events.destroy, workspace);
120 ipc_event_workspace(NULL, workspace, "empty"); // intentional 100 ipc_event_workspace(NULL, workspace, "empty"); // intentional
101 wl_signal_emit(&workspace->node.events.destroy, &workspace->node);
121 102
122 workspace->destroying = true; 103 if (workspace->output) {
123 container_set_dirty(workspace); 104 workspace_detach(workspace);
124
125 if (workspace->parent) {
126 container_remove_child(workspace);
127 } 105 }
106
107 workspace->node.destroying = true;
108 node_set_dirty(&workspace->node);
128} 109}
129 110
130void workspace_consider_destroy(struct sway_container *ws) { 111void workspace_consider_destroy(struct sway_workspace *ws) {
131 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { 112 if (ws->tiling->length == 0 && ws->floating->length == 0
132 return; 113 && output_get_active_workspace(ws->output) != ws) {
133 }
134 struct sway_seat *seat = input_manager_current_seat(input_manager);
135 if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0
136 && seat_get_active_child(seat, ws->parent) != ws) {
137 workspace_begin_destroy(ws); 114 workspace_begin_destroy(ws);
138 } 115 }
139} 116}
@@ -272,59 +249,49 @@ char *workspace_next_name(const char *output_name) {
272 } 249 }
273 // As a fall back, get the current number of active workspaces 250 // As a fall back, get the current number of active workspaces
274 // and return that + 1 for the next workspace's name 251 // and return that + 1 for the next workspace's name
275 int ws_num = root_container.children->length; 252 int ws_num = root->outputs->length;
276 int l = snprintf(NULL, 0, "%d", ws_num); 253 int l = snprintf(NULL, 0, "%d", ws_num);
277 char *name = malloc(l + 1); 254 char *name = malloc(l + 1);
278 if (!sway_assert(name, "Cloud not allocate workspace name")) { 255 if (!sway_assert(name, "Could not allocate workspace name")) {
279 return NULL; 256 return NULL;
280 } 257 }
281 sprintf(name, "%d", ws_num++); 258 sprintf(name, "%d", ws_num++);
282 return name; 259 return name;
283} 260}
284 261
285static bool _workspace_by_number(struct sway_container *view, void *data) { 262static bool _workspace_by_number(struct sway_workspace *ws, void *data) {
286 if (view->type != C_WORKSPACE) {
287 return false;
288 }
289 char *name = data; 263 char *name = data;
290 char *view_name = view->name; 264 char *ws_name = ws->name;
291 while (isdigit(*name)) { 265 while (isdigit(*name)) {
292 if (*name++ != *view_name++) { 266 if (*name++ != *ws_name++) {
293 return false; 267 return false;
294 } 268 }
295 } 269 }
296 return !isdigit(*view_name); 270 return !isdigit(*ws_name);
297} 271}
298 272
299struct sway_container *workspace_by_number(const char* name) { 273struct sway_workspace *workspace_by_number(const char* name) {
300 return root_find_workspace(_workspace_by_number, (void *) name); 274 return root_find_workspace(_workspace_by_number, (void *) name);
301} 275}
302 276
303static bool _workspace_by_name(struct sway_container *view, void *data) { 277static bool _workspace_by_name(struct sway_workspace *ws, void *data) {
304 return (view->type == C_WORKSPACE) && 278 return strcasecmp(ws->name, data) == 0;
305 (strcasecmp(view->name, (char *) data) == 0);
306} 279}
307 280
308struct sway_container *workspace_by_name(const char *name) { 281struct sway_workspace *workspace_by_name(const char *name) {
309 struct sway_seat *seat = input_manager_current_seat(input_manager); 282 struct sway_seat *seat = input_manager_current_seat(input_manager);
310 struct sway_container *current_workspace = NULL, *current_output = NULL; 283 struct sway_workspace *current = seat_get_focused_workspace(seat);
311 struct sway_container *focus = seat_get_focus(seat);
312 if (focus) {
313 current_workspace = focus->type == C_WORKSPACE ?
314 focus : container_parent(focus, C_WORKSPACE);
315 current_output = container_parent(focus, C_OUTPUT);
316 }
317 284
318 if (strcmp(name, "prev") == 0) { 285 if (strcmp(name, "prev") == 0) {
319 return workspace_prev(current_workspace); 286 return workspace_prev(current);
320 } else if (strcmp(name, "prev_on_output") == 0) { 287 } else if (strcmp(name, "prev_on_output") == 0) {
321 return workspace_output_prev(current_output); 288 return workspace_output_prev(current);
322 } else if (strcmp(name, "next") == 0) { 289 } else if (strcmp(name, "next") == 0) {
323 return workspace_next(current_workspace); 290 return workspace_next(current);
324 } else if (strcmp(name, "next_on_output") == 0) { 291 } else if (strcmp(name, "next_on_output") == 0) {
325 return workspace_output_next(current_output); 292 return workspace_output_next(current);
326 } else if (strcmp(name, "current") == 0) { 293 } else if (strcmp(name, "current") == 0) {
327 return current_workspace; 294 return current;
328 } else if (strcasecmp(name, "back_and_forth") == 0) { 295 } else if (strcasecmp(name, "back_and_forth") == 0) {
329 return prev_workspace_name ? 296 return prev_workspace_name ?
330 root_find_workspace(_workspace_by_name, (void*)prev_workspace_name) 297 root_find_workspace(_workspace_by_name, (void*)prev_workspace_name)
@@ -339,97 +306,68 @@ struct sway_container *workspace_by_name(const char *name) {
339 * the end and beginning. If next is false, the previous workspace is returned, 306 * the end and beginning. If next is false, the previous workspace is returned,
340 * otherwise the next one is returned. 307 * otherwise the next one is returned.
341 */ 308 */
342static struct sway_container *workspace_output_prev_next_impl( 309static struct sway_workspace *workspace_output_prev_next_impl(
343 struct sway_container *output, int dir) { 310 struct sway_output *output, int dir) {
344 if (!output) {
345 return NULL;
346 }
347 if (!sway_assert(output->type == C_OUTPUT,
348 "Argument must be an output, is %d", output->type)) {
349 return NULL;
350 }
351
352 struct sway_seat *seat = input_manager_current_seat(input_manager); 311 struct sway_seat *seat = input_manager_current_seat(input_manager);
353 struct sway_container *focus = seat_get_focus_inactive(seat, output); 312 struct sway_workspace *workspace = seat_get_focused_workspace(seat);
354 struct sway_container *workspace = (focus->type == C_WORKSPACE ?
355 focus :
356 container_parent(focus, C_WORKSPACE));
357 313
358 int index = list_find(output->children, workspace); 314 int index = list_find(output->workspaces, workspace);
359 size_t new_index = wrap(index + dir, output->children->length); 315 size_t new_index = wrap(index + dir, output->workspaces->length);
360 return output->children->items[new_index]; 316 return output->workspaces->items[new_index];
361} 317}
362 318
363/** 319/**
364 * Get the previous or next workspace. If the first/last workspace on an output 320 * Get the previous or next workspace. If the first/last workspace on an output
365 * is active, proceed to the previous/next output's previous/next workspace. 321 * is active, proceed to the previous/next output's previous/next workspace.
366 */ 322 */
367static struct sway_container *workspace_prev_next_impl( 323static struct sway_workspace *workspace_prev_next_impl(
368 struct sway_container *workspace, int dir) { 324 struct sway_workspace *workspace, int dir) {
369 if (!workspace) { 325 struct sway_output *output = workspace->output;
370 return NULL; 326 int index = list_find(output->workspaces, workspace);
371 }
372 if (!sway_assert(workspace->type == C_WORKSPACE,
373 "Argument must be a workspace, is %d", workspace->type)) {
374 return NULL;
375 }
376
377 struct sway_container *output = workspace->parent;
378 int index = list_find(output->children, workspace);
379 int new_index = index + dir; 327 int new_index = index + dir;
380 328
381 if (new_index >= 0 && new_index < output->children->length) { 329 if (new_index >= 0 && new_index < output->workspaces->length) {
382 return output->children->items[index + dir]; 330 return output->workspaces->items[new_index];
383 } 331 }
384 332
385 // Look on a different output 333 // Look on a different output
386 int output_index = list_find(root_container.children, output); 334 int output_index = list_find(root->outputs, output);
387 new_index = wrap(output_index + dir, root_container.children->length); 335 new_index = wrap(output_index + dir, root->outputs->length);
388 output = root_container.children->items[new_index]; 336 output = root->outputs->items[new_index];
389 337
390 if (dir == 1) { 338 if (dir == 1) {
391 return output->children->items[0]; 339 return output->workspaces->items[0];
392 } else { 340 } else {
393 return output->children->items[output->children->length - 1]; 341 return output->workspaces->items[output->workspaces->length - 1];
394 } 342 }
395} 343}
396 344
397struct sway_container *workspace_output_next(struct sway_container *current) { 345struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
398 return workspace_output_prev_next_impl(current, 1); 346 return workspace_output_prev_next_impl(current->output, 1);
399} 347}
400 348
401struct sway_container *workspace_next(struct sway_container *current) { 349struct sway_workspace *workspace_next(struct sway_workspace *current) {
402 return workspace_prev_next_impl(current, 1); 350 return workspace_prev_next_impl(current, 1);
403} 351}
404 352
405struct sway_container *workspace_output_prev(struct sway_container *current) { 353struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
406 return workspace_output_prev_next_impl(current, -1); 354 return workspace_output_prev_next_impl(current->output, -1);
407} 355}
408 356
409struct sway_container *workspace_prev(struct sway_container *current) { 357struct sway_workspace *workspace_prev(struct sway_workspace *current) {
410 return workspace_prev_next_impl(current, -1); 358 return workspace_prev_next_impl(current, -1);
411} 359}
412 360
413bool workspace_switch(struct sway_container *workspace, 361bool workspace_switch(struct sway_workspace *workspace,
414 bool no_auto_back_and_forth) { 362 bool no_auto_back_and_forth) {
415 if (!workspace) {
416 return false;
417 }
418 struct sway_seat *seat = input_manager_current_seat(input_manager); 363 struct sway_seat *seat = input_manager_current_seat(input_manager);
419 struct sway_container *focus = 364 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
420 seat_get_focus_inactive(seat, &root_container); 365 struct sway_workspace *active_ws = seat_get_focused_workspace(seat);
421 if (!seat || !focus) {
422 return false;
423 }
424 struct sway_container *active_ws = focus;
425 if (active_ws->type != C_WORKSPACE) {
426 active_ws = container_parent(focus, C_WORKSPACE);
427 }
428 366
429 if (!no_auto_back_and_forth && config->auto_back_and_forth 367 if (!no_auto_back_and_forth && config->auto_back_and_forth
430 && active_ws == workspace 368 && active_ws == workspace
431 && prev_workspace_name) { 369 && prev_workspace_name) {
432 struct sway_container *new_ws = workspace_by_name(prev_workspace_name); 370 struct sway_workspace *new_ws = workspace_by_name(prev_workspace_name);
433 workspace = new_ws ? 371 workspace = new_ws ?
434 new_ws : 372 new_ws :
435 workspace_create(NULL, prev_workspace_name); 373 workspace_create(NULL, prev_workspace_name);
@@ -447,21 +385,21 @@ bool workspace_switch(struct sway_container *workspace,
447 } 385 }
448 386
449 // Move sticky containers to new workspace 387 // Move sticky containers to new workspace
450 struct sway_container *next_output = workspace->parent; 388 struct sway_output *next_output = workspace->output;
451 struct sway_container *next_output_prev_ws = 389 struct sway_workspace *next_output_prev_ws =
452 seat_get_active_child(seat, next_output); 390 output_get_active_workspace(next_output);
453 list_t *floating = next_output_prev_ws->sway_workspace->floating;
454 bool has_sticky = false; 391 bool has_sticky = false;
455 if (workspace != next_output_prev_ws) { 392 if (workspace != next_output_prev_ws) {
456 for (int i = 0; i < floating->length; ++i) { 393 for (int i = 0; i < next_output_prev_ws->floating->length; ++i) {
457 struct sway_container *floater = floating->items[i]; 394 struct sway_container *floater =
395 next_output_prev_ws->floating->items[i];
458 if (floater->is_sticky) { 396 if (floater->is_sticky) {
459 has_sticky = true; 397 has_sticky = true;
460 container_remove_child(floater); 398 container_detach(floater);
461 workspace_add_floating(workspace, floater); 399 workspace_add_floating(workspace, floater);
462 if (floater == focus) { 400 if (&floater->node == focus) {
463 seat_set_focus(seat, NULL); 401 seat_set_focus(seat, NULL);
464 seat_set_focus(seat, floater); 402 seat_set_focus(seat, &floater->node);
465 } 403 }
466 --i; 404 --i;
467 } 405 }
@@ -470,9 +408,9 @@ bool workspace_switch(struct sway_container *workspace,
470 408
471 wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", 409 wlr_log(WLR_DEBUG, "Switching to workspace %p:%s",
472 workspace, workspace->name); 410 workspace, workspace->name);
473 struct sway_container *next = seat_get_focus_inactive(seat, workspace); 411 struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);
474 if (next == NULL) { 412 if (next == NULL) {
475 next = workspace; 413 next = &workspace->node;
476 } 414 }
477 if (has_sticky) { 415 if (has_sticky) {
478 // If there's a sticky container, we might be setting focus to the same 416 // If there's a sticky container, we might be setting focus to the same
@@ -483,35 +421,24 @@ bool workspace_switch(struct sway_container *workspace,
483 workspace_consider_destroy(active_ws); 421 workspace_consider_destroy(active_ws);
484 } 422 }
485 seat_set_focus(seat, next); 423 seat_set_focus(seat, next);
486 struct sway_container *output = container_parent(workspace, C_OUTPUT); 424 arrange_workspace(workspace);
487 arrange_windows(output);
488 return true; 425 return true;
489} 426}
490 427
491bool workspace_is_visible(struct sway_container *ws) { 428bool workspace_is_visible(struct sway_workspace *ws) {
492 if (ws->destroying) { 429 if (ws->node.destroying) {
493 return false; 430 return false;
494 } 431 }
495 struct sway_container *output = container_parent(ws, C_OUTPUT); 432 return output_get_active_workspace(ws->output) == ws;
496 struct sway_seat *seat = input_manager_current_seat(input_manager);
497 struct sway_container *focus = seat_get_focus_inactive(seat, output);
498 if (focus->type != C_WORKSPACE) {
499 focus = container_parent(focus, C_WORKSPACE);
500 }
501 return focus == ws;
502} 433}
503 434
504bool workspace_is_empty(struct sway_container *ws) { 435bool workspace_is_empty(struct sway_workspace *ws) {
505 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { 436 if (ws->tiling->length) {
506 return false;
507 }
508 if (ws->children->length) {
509 return false; 437 return false;
510 } 438 }
511 // Sticky views are not considered to be part of this workspace 439 // Sticky views are not considered to be part of this workspace
512 list_t *floating = ws->sway_workspace->floating; 440 for (int i = 0; i < ws->floating->length; ++i) {
513 for (int i = 0; i < floating->length; ++i) { 441 struct sway_container *floater = ws->floating->items[i];
514 struct sway_container *floater = floating->items[i];
515 if (!floater->is_sticky) { 442 if (!floater->is_sticky) {
516 return false; 443 return false;
517 } 444 }
@@ -523,20 +450,19 @@ static int find_output(const void *id1, const void *id2) {
523 return strcmp(id1, id2) ? 0 : 1; 450 return strcmp(id1, id2) ? 0 : 1;
524} 451}
525 452
526void workspace_output_raise_priority(struct sway_container *workspace, 453void workspace_output_raise_priority(struct sway_workspace *ws,
527 struct sway_container *old_output, struct sway_container *output) { 454 struct sway_output *old_output, struct sway_output *output) {
528 struct sway_workspace *ws = workspace->sway_workspace;
529
530 int old_index = list_seq_find(ws->output_priority, find_output, 455 int old_index = list_seq_find(ws->output_priority, find_output,
531 old_output->name); 456 old_output->wlr_output->name);
532 if (old_index < 0) { 457 if (old_index < 0) {
533 return; 458 return;
534 } 459 }
535 460
536 int new_index = list_seq_find(ws->output_priority, find_output, 461 int new_index = list_seq_find(ws->output_priority, find_output,
537 output->name); 462 output->wlr_output->name);
538 if (new_index < 0) { 463 if (new_index < 0) {
539 list_insert(ws->output_priority, old_index, strdup(output->name)); 464 list_insert(ws->output_priority, old_index,
465 strdup(output->wlr_output->name));
540 } else if (new_index > old_index) { 466 } else if (new_index > old_index) {
541 char *name = ws->output_priority->items[new_index]; 467 char *name = ws->output_priority->items[new_index];
542 list_del(ws->output_priority, new_index); 468 list_del(ws->output_priority, new_index);
@@ -544,29 +470,24 @@ void workspace_output_raise_priority(struct sway_container *workspace,
544 } 470 }
545} 471}
546 472
547void workspace_output_add_priority(struct sway_container *workspace, 473void workspace_output_add_priority(struct sway_workspace *workspace,
548 struct sway_container *output) { 474 struct sway_output *output) {
549 int index = list_seq_find(workspace->sway_workspace->output_priority, 475 int index = list_seq_find(workspace->output_priority,
550 find_output, output->name); 476 find_output, output->wlr_output->name);
551 if (index < 0) { 477 if (index < 0) {
552 list_add(workspace->sway_workspace->output_priority, 478 list_add(workspace->output_priority, strdup(output->wlr_output->name));
553 strdup(output->name));
554 } 479 }
555} 480}
556 481
557static bool _output_by_name(struct sway_container *output, void *data) { 482struct sway_output *workspace_output_get_highest_available(
558 return output->type == C_OUTPUT && strcasecmp(output->name, data) == 0; 483 struct sway_workspace *ws, struct sway_output *exclude) {
559} 484 for (int i = 0; i < ws->output_priority->length; i++) {
560 485 char *name = ws->output_priority->items[i];
561struct sway_container *workspace_output_get_highest_available( 486 if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) {
562 struct sway_container *ws, struct sway_container *exclude) {
563 for (int i = 0; i < ws->sway_workspace->output_priority->length; i++) {
564 char *name = ws->sway_workspace->output_priority->items[i];
565 if (exclude && strcasecmp(name, exclude->name) == 0) {
566 continue; 487 continue;
567 } 488 }
568 489
569 struct sway_container *output = root_find_output(_output_by_name, name); 490 struct sway_output *output = output_by_name(name);
570 if (output) { 491 if (output) {
571 return output; 492 return output;
572 } 493 }
@@ -576,49 +497,42 @@ struct sway_container *workspace_output_get_highest_available(
576} 497}
577 498
578static bool find_urgent_iterator(struct sway_container *con, void *data) { 499static bool find_urgent_iterator(struct sway_container *con, void *data) {
579 return con->type == C_VIEW && view_is_urgent(con->sway_view); 500 return con->view && view_is_urgent(con->view);
580} 501}
581 502
582void workspace_detect_urgent(struct sway_container *workspace) { 503void workspace_detect_urgent(struct sway_workspace *workspace) {
583 bool new_urgent = (bool)workspace_find_container(workspace, 504 bool new_urgent = (bool)workspace_find_container(workspace,
584 find_urgent_iterator, NULL); 505 find_urgent_iterator, NULL);
585 506
586 if (workspace->sway_workspace->urgent != new_urgent) { 507 if (workspace->urgent != new_urgent) {
587 workspace->sway_workspace->urgent = new_urgent; 508 workspace->urgent = new_urgent;
588 ipc_event_workspace(NULL, workspace, "urgent"); 509 ipc_event_workspace(NULL, workspace, "urgent");
589 container_damage_whole(workspace); 510 output_damage_whole(workspace->output);
590 } 511 }
591} 512}
592 513
593void workspace_for_each_container(struct sway_container *ws, 514void workspace_for_each_container(struct sway_workspace *ws,
594 void (*f)(struct sway_container *con, void *data), void *data) { 515 void (*f)(struct sway_container *con, void *data), void *data) {
595 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
596 return;
597 }
598 // Tiling 516 // Tiling
599 for (int i = 0; i < ws->children->length; ++i) { 517 for (int i = 0; i < ws->tiling->length; ++i) {
600 struct sway_container *container = ws->children->items[i]; 518 struct sway_container *container = ws->tiling->items[i];
601 f(container, data); 519 f(container, data);
602 container_for_each_child(container, f, data); 520 container_for_each_child(container, f, data);
603 } 521 }
604 // Floating 522 // Floating
605 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 523 for (int i = 0; i < ws->floating->length; ++i) {
606 struct sway_container *container = 524 struct sway_container *container = ws->floating->items[i];
607 ws->sway_workspace->floating->items[i];
608 f(container, data); 525 f(container, data);
609 container_for_each_child(container, f, data); 526 container_for_each_child(container, f, data);
610 } 527 }
611} 528}
612 529
613struct sway_container *workspace_find_container(struct sway_container *ws, 530struct sway_container *workspace_find_container(struct sway_workspace *ws,
614 bool (*test)(struct sway_container *con, void *data), void *data) { 531 bool (*test)(struct sway_container *con, void *data), void *data) {
615 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
616 return NULL;
617 }
618 struct sway_container *result = NULL; 532 struct sway_container *result = NULL;
619 // Tiling 533 // Tiling
620 for (int i = 0; i < ws->children->length; ++i) { 534 for (int i = 0; i < ws->tiling->length; ++i) {
621 struct sway_container *child = ws->children->items[i]; 535 struct sway_container *child = ws->tiling->items[i];
622 if (test(child, data)) { 536 if (test(child, data)) {
623 return child; 537 return child;
624 } 538 }
@@ -627,8 +541,8 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
627 } 541 }
628 } 542 }
629 // Floating 543 // Floating
630 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 544 for (int i = 0; i < ws->floating->length; ++i) {
631 struct sway_container *child = ws->sway_workspace->floating->items[i]; 545 struct sway_container *child = ws->floating->items[i];
632 if (test(child, data)) { 546 if (test(child, data)) {
633 return child; 547 return child;
634 } 548 }
@@ -639,37 +553,76 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
639 return NULL; 553 return NULL;
640} 554}
641 555
642struct sway_container *workspace_wrap_children(struct sway_container *ws) { 556struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
643 struct sway_container *middle = container_create(C_CONTAINER); 557 struct sway_container *middle = container_create(NULL);
644 middle->layout = ws->layout; 558 middle->layout = ws->layout;
645 while (ws->children->length) { 559 while (ws->tiling->length) {
646 struct sway_container *child = ws->children->items[0]; 560 struct sway_container *child = ws->tiling->items[0];
647 container_remove_child(child); 561 container_detach(child);
648 container_add_child(middle, child); 562 container_add_child(middle, child);
649 } 563 }
650 container_add_child(ws, middle); 564 workspace_add_tiling(ws, middle);
651 return middle; 565 return middle;
652} 566}
653 567
654void workspace_add_floating(struct sway_container *workspace, 568void workspace_detach(struct sway_workspace *workspace) {
655 struct sway_container *con) { 569 struct sway_output *output = workspace->output;
656 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 570 int index = list_find(output->workspaces, workspace);
657 return; 571 if (index != -1) {
572 list_del(output->workspaces, index);
658 } 573 }
659 if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { 574 workspace->output = NULL;
660 return; 575
576 node_set_dirty(&workspace->node);
577 node_set_dirty(&output->node);
578}
579
580static void set_workspace(struct sway_container *container, void *data) {
581 container->workspace = container->parent->workspace;
582}
583
584void workspace_add_tiling(struct sway_workspace *workspace,
585 struct sway_container *con) {
586 if (con->workspace) {
587 container_detach(con);
661 } 588 }
589 list_add(workspace->tiling, con);
590 con->workspace = workspace;
591 container_for_each_child(con, set_workspace, NULL);
592 container_handle_fullscreen_reparent(con);
593 workspace_update_representation(workspace);
594 node_set_dirty(&workspace->node);
595 node_set_dirty(&con->node);
596}
662 597
663 list_add(workspace->sway_workspace->floating, con); 598void workspace_add_floating(struct sway_workspace *workspace,
664 con->parent = workspace; 599 struct sway_container *con) {
665 container_set_dirty(workspace); 600 if (con->workspace) {
666 container_set_dirty(con); 601 container_detach(con);
602 }
603 list_add(workspace->floating, con);
604 con->workspace = workspace;
605 container_for_each_child(con, set_workspace, NULL);
606 container_handle_fullscreen_reparent(con);
607 node_set_dirty(&workspace->node);
608 node_set_dirty(&con->node);
667} 609}
668 610
669void workspace_remove_gaps(struct sway_container *ws) { 611void workspace_insert_tiling(struct sway_workspace *workspace,
670 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { 612 struct sway_container *con, int index) {
671 return; 613 if (con->workspace) {
614 container_detach(con);
672 } 615 }
616 list_insert(workspace->tiling, index, con);
617 con->workspace = workspace;
618 container_for_each_child(con, set_workspace, NULL);
619 container_handle_fullscreen_reparent(con);
620 workspace_update_representation(workspace);
621 node_set_dirty(&workspace->node);
622 node_set_dirty(&con->node);
623}
624
625void workspace_remove_gaps(struct sway_workspace *ws) {
673 if (ws->current_gaps == 0) { 626 if (ws->current_gaps == 0) {
674 return; 627 return;
675 } 628 }
@@ -681,15 +634,12 @@ void workspace_remove_gaps(struct sway_container *ws) {
681 ws->current_gaps = 0; 634 ws->current_gaps = 0;
682} 635}
683 636
684void workspace_add_gaps(struct sway_container *ws) { 637void workspace_add_gaps(struct sway_workspace *ws) {
685 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
686 return;
687 }
688 if (ws->current_gaps > 0) { 638 if (ws->current_gaps > 0) {
689 return; 639 return;
690 } 640 }
691 bool should_apply = 641 bool should_apply =
692 config->edge_gaps || (config->smart_gaps && ws->children->length > 1); 642 config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1);
693 if (!should_apply) { 643 if (!should_apply) {
694 return; 644 return;
695 } 645 }
@@ -708,3 +658,36 @@ void workspace_add_gaps(struct sway_container *ws) {
708 ws->width -= 2 * ws->current_gaps; 658 ws->width -= 2 * ws->current_gaps;
709 ws->height -= 2 * ws->current_gaps; 659 ws->height -= 2 * ws->current_gaps;
710} 660}
661
662struct sway_container *workspace_split(struct sway_workspace *workspace,
663 enum sway_container_layout layout) {
664 if (workspace->tiling->length == 0) {
665 workspace->prev_split_layout = workspace->layout;
666 workspace->layout = layout;
667 return NULL;
668 }
669
670 enum sway_container_layout old_layout = workspace->layout;
671 struct sway_container *middle = workspace_wrap_children(workspace);
672 workspace->layout = layout;
673 middle->layout = old_layout;
674
675 return middle;
676}
677
678void workspace_update_representation(struct sway_workspace *ws) {
679 size_t len = container_build_representation(ws->layout, ws->tiling, NULL);
680 free(ws->representation);
681 ws->representation = calloc(len + 1, sizeof(char));
682 if (!sway_assert(ws->representation, "Unable to allocate title string")) {
683 return;
684 }
685 container_build_representation(ws->layout, ws->tiling, ws->representation);
686}
687
688void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box) {
689 box->x = workspace->x;
690 box->y = workspace->y;
691 box->width = workspace->width;
692 box->height = workspace->height;
693}