summaryrefslogtreecommitdiffstats
path: root/sway/tree
diff options
context:
space:
mode:
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}