aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-30 21:00:10 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-09-05 18:01:43 +1000
commit7586f150c058997d9dde387ea7c091ffa7a3c3c7 (patch)
tree63d19027974c1db62ce3a74ca1d2314eb6d5049b /sway/desktop
parentMerge pull request #2569 from RyanDwyer/deny-reload-repeat (diff)
downloadsway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.gz
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.zst
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.zip
Implement type safe arguments and demote sway_container
This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
Diffstat (limited to 'sway/desktop')
-rw-r--r--sway/desktop/desktop.c31
-rw-r--r--sway/desktop/layer_shell.c23
-rw-r--r--sway/desktop/output.c182
-rw-r--r--sway/desktop/render.c259
-rw-r--r--sway/desktop/transaction.c374
-rw-r--r--sway/desktop/xdg_shell.c47
-rw-r--r--sway/desktop/xdg_shell_v6.c44
-rw-r--r--sway/desktop/xwayland.c50
8 files changed, 547 insertions, 463 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index 72650397..771b58fe 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -4,37 +4,32 @@
4 4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, 5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) { 6 bool whole) {
7 for (int i = 0; i < root_container.children->length; ++i) { 7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_container *cont = root_container.children->items[i]; 8 struct sway_output *output = root->outputs->items[i];
9 if (cont->type == C_OUTPUT) { 9 output_damage_surface(output, lx - output->wlr_output->lx,
10 output_damage_surface(cont->sway_output, 10 ly - output->wlr_output->ly, surface, whole);
11 lx - cont->current.swayc_x, ly - cont->current.swayc_y,
12 surface, whole);
13 }
14 } 11 }
15} 12}
16 13
17void desktop_damage_whole_container(struct sway_container *con) { 14void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root_container.children->length; ++i) { 15 for (int i = 0; i < root->outputs->length; ++i) {
19 struct sway_container *cont = root_container.children->items[i]; 16 struct sway_output *output = root->outputs->items[i];
20 if (cont->type == C_OUTPUT) { 17 output_damage_whole_container(output, con);
21 output_damage_whole_container(cont->sway_output, con);
22 }
23 } 18 }
24} 19}
25 20
26void desktop_damage_box(struct wlr_box *box) { 21void desktop_damage_box(struct wlr_box *box) {
27 for (int i = 0; i < root_container.children->length; ++i) { 22 for (int i = 0; i < root->outputs->length; ++i) {
28 struct sway_container *cont = root_container.children->items[i]; 23 struct sway_output *output = root->outputs->items[i];
29 output_damage_box(cont->sway_output, box); 24 output_damage_box(output, box);
30 } 25 }
31} 26}
32 27
33void desktop_damage_view(struct sway_view *view) { 28void desktop_damage_view(struct sway_view *view) {
34 desktop_damage_whole_container(view->swayc); 29 desktop_damage_whole_container(view->container);
35 struct wlr_box box = { 30 struct wlr_box box = {
36 .x = view->swayc->current.view_x - view->geometry.x, 31 .x = view->container->current.view_x - view->geometry.x,
37 .y = view->swayc->current.view_y - view->geometry.y, 32 .y = view->container->current.view_y - view->geometry.y,
38 .width = view->surface->current.width, 33 .width = view->surface->current.width,
39 .height = view->surface->current.height, 34 .height = view->surface->current.height,
40 }; 35 };
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index a4f7f928..7d254173 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -14,6 +14,7 @@
14#include "sway/output.h" 14#include "sway/output.h"
15#include "sway/server.h" 15#include "sway/server.h"
16#include "sway/tree/arrange.h" 16#include "sway/tree/arrange.h"
17#include "sway/tree/workspace.h"
17#include "log.h" 18#include "log.h"
18 19
19static void apply_exclusive(struct wlr_box *usable_area, 20static void apply_exclusive(struct wlr_box *usable_area,
@@ -176,7 +177,7 @@ void arrange_layers(struct sway_output *output) {
176 sizeof(struct wlr_box)) != 0) { 177 sizeof(struct wlr_box)) != 0) {
177 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); 178 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 179 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_output(output->swayc); 180 arrange_output(output);
180 } 181 }
181 182
182 // Arrange non-exlusive surfaces from top->bottom 183 // Arrange non-exlusive surfaces from top->bottom
@@ -256,7 +257,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
256 return; 257 return;
257 } 258 }
258 struct sway_output *output = wlr_output->data; 259 struct sway_output *output = wlr_output->data;
259 if (output == NULL || output->swayc == NULL) { 260 if (output == NULL) {
260 return; 261 return;
261 } 262 }
262 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 263 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
@@ -283,9 +284,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
283 wl_list_remove(&sway_layer->surface_commit.link); 284 wl_list_remove(&sway_layer->surface_commit.link);
284 if (sway_layer->layer_surface->output != NULL) { 285 if (sway_layer->layer_surface->output != NULL) {
285 struct sway_output *output = sway_layer->layer_surface->output->data; 286 struct sway_output *output = sway_layer->layer_surface->output->data;
286 if (output != NULL && output->swayc != NULL) { 287 if (output != NULL) {
287 arrange_layers(output); 288 arrange_layers(output);
288 arrange_windows(output->swayc); 289 arrange_output(output);
289 transaction_commit_dirty(); 290 transaction_commit_dirty();
290 } 291 }
291 wl_list_remove(&sway_layer->output_destroy.link); 292 wl_list_remove(&sway_layer->output_destroy.link);
@@ -332,23 +333,21 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
332 333
333 if (!layer_surface->output) { 334 if (!layer_surface->output) {
334 // Assign last active output 335 // Assign last active output
335 struct sway_container *output = NULL; 336 struct sway_output *output = NULL;
336 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 337 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
337 if (seat) { 338 if (seat) {
338 output = seat_get_focus_inactive(seat, &root_container); 339 struct sway_workspace *ws = seat_get_focused_workspace(seat);
340 output = ws->output;
339 } 341 }
340 if (!output) { 342 if (!output) {
341 if (!sway_assert(root_container.children->length, 343 if (!sway_assert(root->outputs->length,
342 "cannot auto-assign output for layer")) { 344 "cannot auto-assign output for layer")) {
343 wlr_layer_surface_close(layer_surface); 345 wlr_layer_surface_close(layer_surface);
344 return; 346 return;
345 } 347 }
346 output = root_container.children->items[0]; 348 output = root->outputs->items[0];
347 } 349 }
348 if (output->type != C_OUTPUT) { 350 layer_surface->output = output->wlr_output;
349 output = container_parent(output, C_OUTPUT);
350 }
351 layer_surface->output = output->sway_output->wlr_output;
352 } 351 }
353 352
354 struct sway_layer_surface *sway_layer = 353 struct sway_layer_surface *sway_layer =
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index c30e52a1..c182bad6 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -28,10 +28,10 @@
28#include "sway/tree/view.h" 28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h" 29#include "sway/tree/workspace.h"
30 30
31struct sway_container *output_by_name(const char *name) { 31struct sway_output *output_by_name(const char *name) {
32 for (int i = 0; i < root_container.children->length; ++i) { 32 for (int i = 0; i < root->outputs->length; ++i) {
33 struct sway_container *output = root_container.children->items[i]; 33 struct sway_output *output = root->outputs->items[i];
34 if (strcasecmp(output->name, name) == 0) { 34 if (strcasecmp(output->wlr_output->name, name) == 0) {
35 return output; 35 return output;
36 } 36 }
37 } 37 }
@@ -98,8 +98,8 @@ static bool get_surface_box(struct surface_iterator_data *data,
98 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); 98 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box);
99 99
100 struct wlr_box output_box = { 100 struct wlr_box output_box = {
101 .width = output->swayc->current.swayc_width, 101 .width = output->wlr_output->width,
102 .height = output->swayc->current.swayc_height, 102 .height = output->wlr_output->height,
103 }; 103 };
104 104
105 struct wlr_box intersection; 105 struct wlr_box intersection;
@@ -145,12 +145,12 @@ void output_view_for_each_surface(struct sway_output *output,
145 .user_iterator = iterator, 145 .user_iterator = iterator,
146 .user_data = user_data, 146 .user_data = user_data,
147 .output = output, 147 .output = output,
148 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x 148 .ox = view->container->current.view_x - output->wlr_output->lx
149 - view->geometry.x, 149 - view->geometry.x,
150 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y 150 .oy = view->container->current.view_y - output->wlr_output->ly
151 - view->geometry.y, 151 - view->geometry.y,
152 .width = view->swayc->current.view_width, 152 .width = view->container->current.view_width,
153 .height = view->swayc->current.view_height, 153 .height = view->container->current.view_height,
154 .rotation = 0, // TODO 154 .rotation = 0, // TODO
155 }; 155 };
156 156
@@ -164,12 +164,12 @@ void output_view_for_each_popup(struct sway_output *output,
164 .user_iterator = iterator, 164 .user_iterator = iterator,
165 .user_data = user_data, 165 .user_data = user_data,
166 .output = output, 166 .output = output,
167 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x 167 .ox = view->container->current.view_x - output->wlr_output->lx
168 - view->geometry.x, 168 - view->geometry.x,
169 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y 169 .oy = view->container->current.view_y - output->wlr_output->ly
170 - view->geometry.y, 170 - view->geometry.y,
171 .width = view->swayc->current.view_width, 171 .width = view->container->current.view_width,
172 .height = view->swayc->current.view_height, 172 .height = view->container->current.view_height,
173 .rotation = 0, // TODO 173 .rotation = 0, // TODO
174 }; 174 };
175 175
@@ -197,8 +197,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output,
197 wl_list_for_each(unmanaged_surface, unmanaged, link) { 197 wl_list_for_each(unmanaged_surface, unmanaged, link) {
198 struct wlr_xwayland_surface *xsurface = 198 struct wlr_xwayland_surface *xsurface =
199 unmanaged_surface->wlr_xwayland_surface; 199 unmanaged_surface->wlr_xwayland_surface;
200 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; 200 double ox = unmanaged_surface->lx - output->wlr_output->lx;
201 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; 201 double oy = unmanaged_surface->ly - output->wlr_output->ly;
202 202
203 output_surface_for_each_surface(output, xsurface->surface, ox, oy, 203 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
204 iterator, user_data); 204 iterator, user_data);
@@ -211,8 +211,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
211 void *user_data) { 211 void *user_data) {
212 struct sway_drag_icon *drag_icon; 212 struct sway_drag_icon *drag_icon;
213 wl_list_for_each(drag_icon, drag_icons, link) { 213 wl_list_for_each(drag_icon, drag_icons, link) {
214 double ox = drag_icon->x - output->swayc->x; 214 double ox = drag_icon->x - output->wlr_output->lx;
215 double oy = drag_icon->y - output->swayc->y; 215 double oy = drag_icon->y - output->wlr_output->ly;
216 216
217 if (drag_icon->wlr_drag_icon->mapped) { 217 if (drag_icon->wlr_drag_icon->mapped) {
218 output_surface_for_each_surface(output, 218 output_surface_for_each_surface(output,
@@ -229,19 +229,13 @@ static void scale_box(struct wlr_box *box, float scale) {
229 box->height *= scale; 229 box->height *= scale;
230} 230}
231 231
232struct sway_container *output_get_active_workspace(struct sway_output *output) { 232struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
233 struct sway_seat *seat = input_manager_current_seat(input_manager); 233 struct sway_seat *seat = input_manager_current_seat(input_manager);
234 struct sway_container *focus = 234 struct sway_node *focus = seat_get_active_child(seat, &output->node);
235 seat_get_focus_inactive(seat, output->swayc);
236 if (!focus) { 235 if (!focus) {
237 // We've never been to this output before 236 return output->workspaces->items[0];
238 focus = output->swayc->current.children->items[0];
239 } 237 }
240 struct sway_container *workspace = focus; 238 return focus->sway_workspace;
241 if (workspace->type != C_WORKSPACE) {
242 workspace = container_parent(workspace, C_WORKSPACE);
243 }
244 return workspace;
245} 239}
246 240
247bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 241bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
@@ -255,8 +249,8 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
255 struct sway_layer_surface *sway_layer_surface = 249 struct sway_layer_surface *sway_layer_surface =
256 layer_from_wlr_layer_surface(wlr_layer_surface); 250 layer_from_wlr_layer_surface(wlr_layer_surface);
257 pixman_box32_t output_box = { 251 pixman_box32_t output_box = {
258 .x2 = output->swayc->current.swayc_width, 252 .x2 = output->wlr_output->width,
259 .y2 = output->swayc->current.swayc_height, 253 .y2 = output->wlr_output->height,
260 }; 254 };
261 pixman_region32_t surface_opaque_box; 255 pixman_region32_t surface_opaque_box;
262 pixman_region32_init(&surface_opaque_box); 256 pixman_region32_init(&surface_opaque_box);
@@ -307,15 +301,15 @@ struct send_frame_done_data {
307 301
308static void send_frame_done_container_iterator(struct sway_container *con, 302static void send_frame_done_container_iterator(struct sway_container *con,
309 void *_data) { 303 void *_data) {
310 if (con->type != C_VIEW) { 304 if (!con->view) {
311 return; 305 return;
312 } 306 }
313 if (!view_is_visible(con->sway_view)) { 307 if (!view_is_visible(con->view)) {
314 return; 308 return;
315 } 309 }
316 310
317 struct send_frame_done_data *data = _data; 311 struct send_frame_done_data *data = _data;
318 output_view_for_each_surface(data->output, con->sway_view, 312 output_view_for_each_surface(data->output, con->view,
319 send_frame_done_iterator, data->when); 313 send_frame_done_iterator, data->when);
320} 314}
321 315
@@ -328,15 +322,14 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
328 .output = output, 322 .output = output,
329 .when = when, 323 .when = when,
330 }; 324 };
331 struct sway_container *workspace = output_get_active_workspace(output); 325 struct sway_workspace *workspace = output_get_active_workspace(output);
332 if (workspace->current.ws_fullscreen) { 326 if (workspace->current.fullscreen) {
333 send_frame_done_container_iterator( 327 send_frame_done_container_iterator(
334 workspace->current.ws_fullscreen, &data); 328 workspace->current.fullscreen, &data);
335 container_for_each_child(workspace->current.ws_fullscreen, 329 container_for_each_child(workspace->current.fullscreen,
336 send_frame_done_container_iterator, &data); 330 send_frame_done_container_iterator, &data);
337#ifdef HAVE_XWAYLAND 331#ifdef HAVE_XWAYLAND
338 send_frame_done_unmanaged(output, 332 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
339 &root_container.sway_root->xwayland_unmanaged, when);
340#endif 333#endif
341 } else { 334 } else {
342 send_frame_done_layer(output, 335 send_frame_done_layer(output,
@@ -348,8 +341,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
348 send_frame_done_container_iterator, &data); 341 send_frame_done_container_iterator, &data);
349 342
350#ifdef HAVE_XWAYLAND 343#ifdef HAVE_XWAYLAND
351 send_frame_done_unmanaged(output, 344 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
352 &root_container.sway_root->xwayland_unmanaged, when);
353#endif 345#endif
354 send_frame_done_layer(output, 346 send_frame_done_layer(output,
355 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); 347 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when);
@@ -358,8 +350,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
358send_frame_overlay: 350send_frame_overlay:
359 send_frame_done_layer(output, 351 send_frame_done_layer(output,
360 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); 352 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when);
361 send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, 353 send_frame_done_drag_icons(output, &root->drag_icons, when);
362 when);
363} 354}
364 355
365static void damage_handle_frame(struct wl_listener *listener, void *data) { 356static void damage_handle_frame(struct wl_listener *listener, void *data) {
@@ -391,7 +382,11 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
391} 382}
392 383
393void output_damage_whole(struct sway_output *output) { 384void output_damage_whole(struct sway_output *output) {
394 wlr_output_damage_add_whole(output->damage); 385 // The output can exist with no wlr_output if it's just been disconnected
386 // and the transaction to evacuate it has't completed yet.
387 if (output && output->wlr_output) {
388 wlr_output_damage_add_whole(output->damage);
389 }
395} 390}
396 391
397static void damage_surface_iterator(struct sway_output *output, 392static void damage_surface_iterator(struct sway_output *output,
@@ -446,14 +441,9 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
446 441
447static void output_damage_view(struct sway_output *output, 442static void output_damage_view(struct sway_output *output,
448 struct sway_view *view, bool whole) { 443 struct sway_view *view, bool whole) {
449 if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
450 return;
451 }
452
453 if (!view_is_visible(view)) { 444 if (!view_is_visible(view)) {
454 return; 445 return;
455 } 446 }
456
457 output_view_for_each_surface(output, view, damage_surface_iterator, &whole); 447 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
458} 448}
459 449
@@ -466,31 +456,29 @@ void output_damage_from_view(struct sway_output *output,
466void output_damage_box(struct sway_output *output, struct wlr_box *_box) { 456void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
467 struct wlr_box box; 457 struct wlr_box box;
468 memcpy(&box, _box, sizeof(struct wlr_box)); 458 memcpy(&box, _box, sizeof(struct wlr_box));
469 box.x -= output->swayc->current.swayc_x; 459 box.x -= output->wlr_output->lx;
470 box.y -= output->swayc->current.swayc_y; 460 box.y -= output->wlr_output->ly;
471 scale_box(&box, output->wlr_output->scale); 461 scale_box(&box, output->wlr_output->scale);
472 wlr_output_damage_add_box(output->damage, &box); 462 wlr_output_damage_add_box(output->damage, &box);
473} 463}
474 464
475static void output_damage_whole_container_iterator(struct sway_container *con, 465static void output_damage_whole_container_iterator(struct sway_container *con,
476 void *data) { 466 void *data) {
477 struct sway_output *output = data; 467 if (!sway_assert(con->view, "expected a view")) {
478
479 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
480 return; 468 return;
481 } 469 }
482 470 struct sway_output *output = data;
483 output_damage_view(output, con->sway_view, true); 471 output_damage_view(output, con->view, true);
484} 472}
485 473
486void output_damage_whole_container(struct sway_output *output, 474void output_damage_whole_container(struct sway_output *output,
487 struct sway_container *con) { 475 struct sway_container *con) {
488 // Pad the box by 1px, because the width is a double and might be a fraction 476 // Pad the box by 1px, because the width is a double and might be a fraction
489 struct wlr_box box = { 477 struct wlr_box box = {
490 .x = con->current.swayc_x - output->wlr_output->lx - 1, 478 .x = con->current.con_x - output->wlr_output->lx - 1,
491 .y = con->current.swayc_y - output->wlr_output->ly - 1, 479 .y = con->current.con_y - output->wlr_output->ly - 1,
492 .width = con->current.swayc_width + 2, 480 .width = con->current.con_width + 2,
493 .height = con->current.swayc_height + 2, 481 .height = con->current.con_height + 2,
494 }; 482 };
495 scale_box(&box, output->wlr_output->scale); 483 scale_box(&box, output->wlr_output->scale);
496 wlr_output_damage_add_box(output->damage, &box); 484 wlr_output_damage_add_box(output->damage, &box);
@@ -499,44 +487,48 @@ void output_damage_whole_container(struct sway_output *output,
499static void damage_handle_destroy(struct wl_listener *listener, void *data) { 487static void damage_handle_destroy(struct wl_listener *listener, void *data) {
500 struct sway_output *output = 488 struct sway_output *output =
501 wl_container_of(listener, output, damage_destroy); 489 wl_container_of(listener, output, damage_destroy);
502 output_begin_destroy(output->swayc); 490 output_disable(output);
491 transaction_commit_dirty();
503} 492}
504 493
505static void handle_destroy(struct wl_listener *listener, void *data) { 494static void handle_destroy(struct wl_listener *listener, void *data) {
506 struct sway_output *output = wl_container_of(listener, output, destroy); 495 struct sway_output *output = wl_container_of(listener, output, destroy);
507 wl_signal_emit(&output->events.destroy, output); 496 wl_signal_emit(&output->events.destroy, output);
508 497
509 if (output->swayc) { 498 if (output->enabled) {
510 output_begin_destroy(output->swayc); 499 output_disable(output);
511 } 500 }
501 output_begin_destroy(output);
512 502
513 wl_list_remove(&output->link); 503 transaction_commit_dirty();
514 wl_list_remove(&output->destroy.link);
515 output->wlr_output->data = NULL;
516 free(output);
517
518 arrange_windows(&root_container);
519} 504}
520 505
521static void handle_mode(struct wl_listener *listener, void *data) { 506static void handle_mode(struct wl_listener *listener, void *data) {
522 struct sway_output *output = wl_container_of(listener, output, mode); 507 struct sway_output *output = wl_container_of(listener, output, mode);
523 arrange_layers(output); 508 arrange_layers(output);
524 arrange_windows(output->swayc); 509 arrange_output(output);
525 transaction_commit_dirty(); 510 transaction_commit_dirty();
526} 511}
527 512
528static void handle_transform(struct wl_listener *listener, void *data) { 513static void handle_transform(struct wl_listener *listener, void *data) {
529 struct sway_output *output = wl_container_of(listener, output, transform); 514 struct sway_output *output = wl_container_of(listener, output, transform);
530 arrange_layers(output); 515 arrange_layers(output);
531 arrange_windows(output->swayc); 516 arrange_output(output);
532 transaction_commit_dirty(); 517 transaction_commit_dirty();
533} 518}
534 519
520static void update_textures(struct sway_container *con, void *data) {
521 container_update_title_textures(con);
522 if (con->view) {
523 view_update_marks_textures(con->view);
524 }
525}
526
535static void handle_scale(struct wl_listener *listener, void *data) { 527static void handle_scale(struct wl_listener *listener, void *data) {
536 struct sway_output *output = wl_container_of(listener, output, scale); 528 struct sway_output *output = wl_container_of(listener, output, scale);
537 arrange_layers(output); 529 arrange_layers(output);
538 container_update_textures_recursive(output->swayc); 530 output_for_each_container(output, update_textures, NULL);
539 arrange_windows(output->swayc); 531 arrange_output(output);
540 transaction_commit_dirty(); 532 transaction_commit_dirty();
541} 533}
542 534
@@ -545,57 +537,27 @@ void handle_new_output(struct wl_listener *listener, void *data) {
545 struct wlr_output *wlr_output = data; 537 struct wlr_output *wlr_output = data;
546 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 538 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
547 539
548 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 540 struct sway_output *output = output_create(wlr_output);
549 if (!output) { 541 if (!output) {
550 return; 542 return;
551 } 543 }
552 output->wlr_output = wlr_output;
553 wlr_output->data = output;
554 output->server = server; 544 output->server = server;
555 output->damage = wlr_output_damage_create(wlr_output); 545 output->damage = wlr_output_damage_create(wlr_output);
556
557 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
558 output->destroy.notify = handle_destroy; 546 output->destroy.notify = handle_destroy;
559 547
560 wl_list_insert(&root_container.sway_root->all_outputs, &output->link); 548 struct output_config *oc = output_find_config(output);
561
562 output_enable(output);
563}
564
565void output_enable(struct sway_output *output) {
566 struct wlr_output *wlr_output = output->wlr_output;
567
568 if (!sway_assert(output->swayc == NULL, "output is already enabled")) {
569 return;
570 }
571
572 output->swayc = output_create(output);
573 if (!output->swayc) {
574 // Output is disabled
575 return;
576 }
577 549
578 size_t len = sizeof(output->layers) / sizeof(output->layers[0]); 550 if (oc && oc->enabled) {
579 for (size_t i = 0; i < len; ++i) { 551 output_enable(output, oc);
580 wl_list_init(&output->layers[i]);
581 } 552 }
582 wl_signal_init(&output->events.destroy);
583 553
584 input_manager_configure_xcursor(input_manager); 554 transaction_commit_dirty();
555}
585 556
586 wl_signal_add(&wlr_output->events.mode, &output->mode); 557void output_add_listeners(struct sway_output *output) {
587 output->mode.notify = handle_mode; 558 output->mode.notify = handle_mode;
588 wl_signal_add(&wlr_output->events.transform, &output->transform);
589 output->transform.notify = handle_transform; 559 output->transform.notify = handle_transform;
590 wl_signal_add(&wlr_output->events.scale, &output->scale);
591 output->scale.notify = handle_scale; 560 output->scale.notify = handle_scale;
592
593 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
594 output->damage_frame.notify = damage_handle_frame; 561 output->damage_frame.notify = damage_handle_frame;
595 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
596 output->damage_destroy.notify = damage_handle_destroy; 562 output->damage_destroy.notify = damage_handle_destroy;
597
598 arrange_layers(output);
599 arrange_windows(&root_container);
600 transaction_commit_dirty();
601} 563}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 695213eb..99b2cf3d 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -193,10 +193,10 @@ static void render_view_toplevels(struct sway_view *view,
193 .alpha = alpha, 193 .alpha = alpha,
194 }; 194 };
195 // Render all toplevels without descending into popups 195 // Render all toplevels without descending into popups
196 double ox = 196 double ox = view->container->current.view_x -
197 view->swayc->current.view_x - output->wlr_output->lx - view->geometry.x; 197 output->wlr_output->lx - view->geometry.x;
198 double oy = 198 double oy = view->container->current.view_y -
199 view->swayc->current.view_y - output->wlr_output->ly - view->geometry.y; 199 output->wlr_output->ly - view->geometry.y;
200 output_surface_for_each_surface(output, view->surface, ox, oy, 200 output_surface_for_each_surface(output, view->surface, ox, oy,
201 render_surface_iterator, &data); 201 render_surface_iterator, &data);
202} 202}
@@ -229,17 +229,17 @@ static void render_saved_view(struct sway_view *view,
229 return; 229 return;
230 } 230 }
231 struct wlr_box box = { 231 struct wlr_box box = {
232 .x = view->swayc->current.view_x - output->swayc->current.swayc_x - 232 .x = view->container->current.view_x - output->wlr_output->lx -
233 view->saved_geometry.x, 233 view->saved_geometry.x,
234 .y = view->swayc->current.view_y - output->swayc->current.swayc_y - 234 .y = view->container->current.view_y - output->wlr_output->ly -
235 view->saved_geometry.y, 235 view->saved_geometry.y,
236 .width = view->saved_buffer_width, 236 .width = view->saved_buffer_width,
237 .height = view->saved_buffer_height, 237 .height = view->saved_buffer_height,
238 }; 238 };
239 239
240 struct wlr_box output_box = { 240 struct wlr_box output_box = {
241 .width = output->swayc->current.swayc_width, 241 .width = output->wlr_output->width,
242 .height = output->swayc->current.swayc_height, 242 .height = output->wlr_output->height,
243 }; 243 };
244 244
245 struct wlr_box intersection; 245 struct wlr_box intersection;
@@ -263,14 +263,14 @@ static void render_saved_view(struct sway_view *view,
263 */ 263 */
264static void render_view(struct sway_output *output, pixman_region32_t *damage, 264static void render_view(struct sway_output *output, pixman_region32_t *damage,
265 struct sway_container *con, struct border_colors *colors) { 265 struct sway_container *con, struct border_colors *colors) {
266 struct sway_view *view = con->sway_view; 266 struct sway_view *view = con->view;
267 if (view->saved_buffer) { 267 if (view->saved_buffer) {
268 render_saved_view(view, output, damage, view->swayc->alpha); 268 render_saved_view(view, output, damage, view->container->alpha);
269 } else { 269 } else {
270 render_view_toplevels(view, output, damage, view->swayc->alpha); 270 render_view_toplevels(view, output, damage, view->container->alpha);
271 } 271 }
272 272
273 if (view->swayc->current.using_csd) { 273 if (view->container->current.using_csd) {
274 return; 274 return;
275 } 275 }
276 276
@@ -283,7 +283,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
283 if (state->border_left) { 283 if (state->border_left) {
284 memcpy(&color, colors->child_border, sizeof(float) * 4); 284 memcpy(&color, colors->child_border, sizeof(float) * 4);
285 premultiply_alpha(color, con->alpha); 285 premultiply_alpha(color, con->alpha);
286 box.x = state->swayc_x; 286 box.x = state->con_x;
287 box.y = state->view_y; 287 box.y = state->view_y;
288 box.width = state->border_thickness; 288 box.width = state->border_thickness;
289 box.height = state->view_height; 289 box.height = state->view_height;
@@ -291,9 +291,12 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
291 render_rect(output->wlr_output, damage, &box, color); 291 render_rect(output->wlr_output, damage, &box, color);
292 } 292 }
293 293
294 list_t *siblings = container_get_current_siblings(con);
295 enum sway_container_layout layout =
296 container_current_parent_layout(con);
297
294 if (state->border_right) { 298 if (state->border_right) {
295 if (state->parent->current.children->length == 1 299 if (siblings->length == 1 && layout == L_HORIZ) {
296 && state->parent->current.layout == L_HORIZ) {
297 memcpy(&color, colors->indicator, sizeof(float) * 4); 300 memcpy(&color, colors->indicator, sizeof(float) * 4);
298 } else { 301 } else {
299 memcpy(&color, colors->child_border, sizeof(float) * 4); 302 memcpy(&color, colors->child_border, sizeof(float) * 4);
@@ -308,16 +311,15 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
308 } 311 }
309 312
310 if (state->border_bottom) { 313 if (state->border_bottom) {
311 if (state->parent->current.children->length == 1 314 if (siblings->length == 1 && layout == L_VERT) {
312 && con->current.parent->current.layout == L_VERT) {
313 memcpy(&color, colors->indicator, sizeof(float) * 4); 315 memcpy(&color, colors->indicator, sizeof(float) * 4);
314 } else { 316 } else {
315 memcpy(&color, colors->child_border, sizeof(float) * 4); 317 memcpy(&color, colors->child_border, sizeof(float) * 4);
316 } 318 }
317 premultiply_alpha(color, con->alpha); 319 premultiply_alpha(color, con->alpha);
318 box.x = state->swayc_x; 320 box.x = state->con_x;
319 box.y = state->view_y + state->view_height; 321 box.y = state->view_y + state->view_height;
320 box.width = state->swayc_width; 322 box.width = state->con_width;
321 box.height = state->border_thickness; 323 box.height = state->border_thickness;
322 scale_box(&box, output_scale); 324 scale_box(&box, output_scale);
323 render_rect(output->wlr_output, damage, &box, color); 325 render_rect(output->wlr_output, damage, &box, color);
@@ -344,12 +346,12 @@ static void render_titlebar(struct sway_output *output,
344 float color[4]; 346 float color[4];
345 struct sway_container_state *state = &con->current; 347 struct sway_container_state *state = &con->current;
346 float output_scale = output->wlr_output->scale; 348 float output_scale = output->wlr_output->scale;
347 enum sway_container_layout layout = state->parent->current.layout; 349 enum sway_container_layout layout = container_current_parent_layout(con);
348 list_t *children = state->parent->current.children; 350 list_t *children = container_get_current_siblings(con);
349 bool is_last_child = children->length == 0 || 351 bool is_last_child = children->length == 0 ||
350 children->items[children->length - 1] == con; 352 children->items[children->length - 1] == con;
351 double output_x = output->swayc->current.swayc_x; 353 double output_x = output->wlr_output->lx;
352 double output_y = output->swayc->current.swayc_y; 354 double output_y = output->wlr_output->ly;
353 355
354 // Single pixel bar above title 356 // Single pixel bar above title
355 memcpy(&color, colors->border, sizeof(float) * 4); 357 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -366,7 +368,7 @@ static void render_titlebar(struct sway_output *output,
366 bool connects_sides = false; 368 bool connects_sides = false;
367 if (layout == L_HORIZ || layout == L_VERT || 369 if (layout == L_HORIZ || layout == L_VERT ||
368 (layout == L_STACKED && is_last_child)) { 370 (layout == L_STACKED && is_last_child)) {
369 if (con->type == C_VIEW) { 371 if (con->view) {
370 left_offset = state->border_left * state->border_thickness; 372 left_offset = state->border_left * state->border_thickness;
371 right_offset = state->border_right * state->border_thickness; 373 right_offset = state->border_right * state->border_thickness;
372 connects_sides = true; 374 connects_sides = true;
@@ -542,14 +544,22 @@ static void render_top_border(struct sway_output *output,
542 // Child border - top edge 544 // Child border - top edge
543 memcpy(&color, colors->child_border, sizeof(float) * 4); 545 memcpy(&color, colors->child_border, sizeof(float) * 4);
544 premultiply_alpha(color, con->alpha); 546 premultiply_alpha(color, con->alpha);
545 box.x = state->swayc_x; 547 box.x = state->con_x;
546 box.y = state->swayc_y; 548 box.y = state->con_y;
547 box.width = state->swayc_width; 549 box.width = state->con_width;
548 box.height = state->border_thickness; 550 box.height = state->border_thickness;
549 scale_box(&box, output_scale); 551 scale_box(&box, output_scale);
550 render_rect(output->wlr_output, output_damage, &box, color); 552 render_rect(output->wlr_output, output_damage, &box, color);
551} 553}
552 554
555struct parent_data {
556 enum sway_container_layout layout;
557 struct wlr_box box;
558 list_t *children;
559 bool focused;
560 struct sway_container *active_child;
561};
562
553static void render_container(struct sway_output *output, 563static void render_container(struct sway_output *output,
554 pixman_region32_t *damage, struct sway_container *con, bool parent_focused); 564 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
555 565
@@ -559,14 +569,13 @@ static void render_container(struct sway_output *output,
559 * Wrap child views in borders and leave child containers borderless because 569 * Wrap child views in borders and leave child containers borderless because
560 * they'll apply their own borders to their children. 570 * they'll apply their own borders to their children.
561 */ 571 */
562static void render_container_simple(struct sway_output *output, 572static void render_containers_linear(struct sway_output *output,
563 pixman_region32_t *damage, struct sway_container *con, 573 pixman_region32_t *damage, struct parent_data *parent) {
564 bool parent_focused) { 574 for (int i = 0; i < parent->children->length; ++i) {
565 for (int i = 0; i < con->current.children->length; ++i) { 575 struct sway_container *child = parent->children->items[i];
566 struct sway_container *child = con->current.children->items[i]; 576
567 577 if (child->view) {
568 if (child->type == C_VIEW) { 578 struct sway_view *view = child->view;
569 struct sway_view *view = child->sway_view;
570 struct border_colors *colors; 579 struct border_colors *colors;
571 struct wlr_texture *title_texture; 580 struct wlr_texture *title_texture;
572 struct wlr_texture *marks_texture; 581 struct wlr_texture *marks_texture;
@@ -576,11 +585,11 @@ static void render_container_simple(struct sway_output *output,
576 colors = &config->border_colors.urgent; 585 colors = &config->border_colors.urgent;
577 title_texture = child->title_urgent; 586 title_texture = child->title_urgent;
578 marks_texture = view->marks_urgent; 587 marks_texture = view->marks_urgent;
579 } else if (state->focused || parent_focused) { 588 } else if (state->focused || parent->focused) {
580 colors = &config->border_colors.focused; 589 colors = &config->border_colors.focused;
581 title_texture = child->title_focused; 590 title_texture = child->title_focused;
582 marks_texture = view->marks_focused; 591 marks_texture = view->marks_focused;
583 } else if (con->current.focused_inactive_child == child) { 592 } else if (child == parent->active_child) {
584 colors = &config->border_colors.focused_inactive; 593 colors = &config->border_colors.focused_inactive;
585 title_texture = child->title_focused_inactive; 594 title_texture = child->title_focused_inactive;
586 marks_texture = view->marks_focused_inactive; 595 marks_texture = view->marks_focused_inactive;
@@ -590,10 +599,10 @@ static void render_container_simple(struct sway_output *output,
590 marks_texture = view->marks_unfocused; 599 marks_texture = view->marks_unfocused;
591 } 600 }
592 601
593 if (!view->swayc->current.using_csd) { 602 if (!view->container->current.using_csd) {
594 if (state->border == B_NORMAL) { 603 if (state->border == B_NORMAL) {
595 render_titlebar(output, damage, child, state->swayc_x, 604 render_titlebar(output, damage, child, state->con_x,
596 state->swayc_y, state->swayc_width, colors, 605 state->con_y, state->con_width, colors,
597 title_texture, marks_texture); 606 title_texture, marks_texture);
598 } else { 607 } else {
599 render_top_border(output, damage, child, colors); 608 render_top_border(output, damage, child, colors);
@@ -602,7 +611,7 @@ static void render_container_simple(struct sway_output *output,
602 render_view(output, damage, child, colors); 611 render_view(output, damage, child, colors);
603 } else { 612 } else {
604 render_container(output, damage, child, 613 render_container(output, damage, child,
605 parent_focused || child->current.focused); 614 parent->focused || child->current.focused);
606 } 615 }
607 } 616 }
608} 617}
@@ -610,22 +619,19 @@ static void render_container_simple(struct sway_output *output,
610/** 619/**
611 * Render a container's children using the L_TABBED layout. 620 * Render a container's children using the L_TABBED layout.
612 */ 621 */
613static void render_container_tabbed(struct sway_output *output, 622static void render_containers_tabbed(struct sway_output *output,
614 pixman_region32_t *damage, struct sway_container *con, 623 pixman_region32_t *damage, struct parent_data *parent) {
615 bool parent_focused) { 624 if (!parent->children->length) {
616 if (!con->current.children->length) {
617 return; 625 return;
618 } 626 }
619 struct sway_container_state *pstate = &con->current; 627 struct sway_container *current = parent->active_child;
620 struct sway_container *current = pstate->focused_inactive_child;
621 struct border_colors *current_colors = &config->border_colors.unfocused; 628 struct border_colors *current_colors = &config->border_colors.unfocused;
622 629 int tab_width = parent->box.width / parent->children->length;
623 int tab_width = (pstate->swayc_width) / pstate->children->length;
624 630
625 // Render tabs 631 // Render tabs
626 for (int i = 0; i < pstate->children->length; ++i) { 632 for (int i = 0; i < parent->children->length; ++i) {
627 struct sway_container *child = pstate->children->items[i]; 633 struct sway_container *child = parent->children->items[i];
628 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 634 struct sway_view *view = child->view;
629 struct sway_container_state *cstate = &child->current; 635 struct sway_container_state *cstate = &child->current;
630 struct border_colors *colors; 636 struct border_colors *colors;
631 struct wlr_texture *title_texture; 637 struct wlr_texture *title_texture;
@@ -637,11 +643,11 @@ static void render_container_tabbed(struct sway_output *output,
637 colors = &config->border_colors.urgent; 643 colors = &config->border_colors.urgent;
638 title_texture = child->title_urgent; 644 title_texture = child->title_urgent;
639 marks_texture = view ? view->marks_urgent : NULL; 645 marks_texture = view ? view->marks_urgent : NULL;
640 } else if (cstate->focused || parent_focused) { 646 } else if (cstate->focused || parent->focused) {
641 colors = &config->border_colors.focused; 647 colors = &config->border_colors.focused;
642 title_texture = child->title_focused; 648 title_texture = child->title_focused;
643 marks_texture = view ? view->marks_focused : NULL; 649 marks_texture = view ? view->marks_focused : NULL;
644 } else if (child == pstate->focused_inactive_child) { 650 } else if (child == parent->active_child) {
645 colors = &config->border_colors.focused_inactive; 651 colors = &config->border_colors.focused_inactive;
646 title_texture = child->title_focused_inactive; 652 title_texture = child->title_focused_inactive;
647 marks_texture = view ? view->marks_focused_inactive : NULL; 653 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -651,14 +657,14 @@ static void render_container_tabbed(struct sway_output *output,
651 marks_texture = view ? view->marks_unfocused : NULL; 657 marks_texture = view ? view->marks_unfocused : NULL;
652 } 658 }
653 659
654 int x = cstate->swayc_x + tab_width * i; 660 int x = cstate->con_x + tab_width * i;
655 661
656 // Make last tab use the remaining width of the parent 662 // Make last tab use the remaining width of the parent
657 if (i == pstate->children->length - 1) { 663 if (i == parent->children->length - 1) {
658 tab_width = pstate->swayc_width - tab_width * i; 664 tab_width = parent->box.width - tab_width * i;
659 } 665 }
660 666
661 render_titlebar(output, damage, child, x, pstate->swayc_y, tab_width, 667 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
662 colors, title_texture, marks_texture); 668 colors, title_texture, marks_texture);
663 669
664 if (child == current) { 670 if (child == current) {
@@ -667,33 +673,30 @@ static void render_container_tabbed(struct sway_output *output,
667 } 673 }
668 674
669 // Render surface and left/right/bottom borders 675 // Render surface and left/right/bottom borders
670 if (current->type == C_VIEW) { 676 if (current->view) {
671 render_view(output, damage, current, current_colors); 677 render_view(output, damage, current, current_colors);
672 } else { 678 } else {
673 render_container(output, damage, current, 679 render_container(output, damage, current,
674 parent_focused || current->current.focused); 680 parent->focused || current->current.focused);
675 } 681 }
676} 682}
677 683
678/** 684/**
679 * Render a container's children using the L_STACKED layout. 685 * Render a container's children using the L_STACKED layout.
680 */ 686 */
681static void render_container_stacked(struct sway_output *output, 687static void render_containers_stacked(struct sway_output *output,
682 pixman_region32_t *damage, struct sway_container *con, 688 pixman_region32_t *damage, struct parent_data *parent) {
683 bool parent_focused) { 689 if (!parent->children->length) {
684 if (!con->current.children->length) {
685 return; 690 return;
686 } 691 }
687 struct sway_container_state *pstate = &con->current; 692 struct sway_container *current = parent->active_child;
688 struct sway_container *current = pstate->focused_inactive_child;
689 struct border_colors *current_colors = &config->border_colors.unfocused; 693 struct border_colors *current_colors = &config->border_colors.unfocused;
690
691 size_t titlebar_height = container_titlebar_height(); 694 size_t titlebar_height = container_titlebar_height();
692 695
693 // Render titles 696 // Render titles
694 for (int i = 0; i < pstate->children->length; ++i) { 697 for (int i = 0; i < parent->children->length; ++i) {
695 struct sway_container *child = pstate->children->items[i]; 698 struct sway_container *child = parent->children->items[i];
696 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 699 struct sway_view *view = child->view;
697 struct sway_container_state *cstate = &child->current; 700 struct sway_container_state *cstate = &child->current;
698 struct border_colors *colors; 701 struct border_colors *colors;
699 struct wlr_texture *title_texture; 702 struct wlr_texture *title_texture;
@@ -705,11 +708,11 @@ static void render_container_stacked(struct sway_output *output,
705 colors = &config->border_colors.urgent; 708 colors = &config->border_colors.urgent;
706 title_texture = child->title_urgent; 709 title_texture = child->title_urgent;
707 marks_texture = view ? view->marks_urgent : NULL; 710 marks_texture = view ? view->marks_urgent : NULL;
708 } else if (cstate->focused || parent_focused) { 711 } else if (cstate->focused || parent->focused) {
709 colors = &config->border_colors.focused; 712 colors = &config->border_colors.focused;
710 title_texture = child->title_focused; 713 title_texture = child->title_focused;
711 marks_texture = view ? view->marks_focused : NULL; 714 marks_texture = view ? view->marks_focused : NULL;
712 } else if (child == pstate->focused_inactive_child) { 715 } else if (child == parent->active_child) {
713 colors = &config->border_colors.focused_inactive; 716 colors = &config->border_colors.focused_inactive;
714 title_texture = child->title_focused_inactive; 717 title_texture = child->title_focused_inactive;
715 marks_texture = view ? view->marks_focused_inactive : NULL; 718 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -719,9 +722,9 @@ static void render_container_stacked(struct sway_output *output,
719 marks_texture = view ? view->marks_unfocused : NULL; 722 marks_texture = view ? view->marks_unfocused : NULL;
720 } 723 }
721 724
722 int y = pstate->swayc_y + titlebar_height * i; 725 int y = parent->box.y + titlebar_height * i;
723 render_titlebar(output, damage, child, pstate->swayc_x, y, 726 render_titlebar(output, damage, child, parent->box.x, y,
724 pstate->swayc_width, colors, title_texture, marks_texture); 727 parent->box.width, colors, title_texture, marks_texture);
725 728
726 if (child == current) { 729 if (child == current) {
727 current_colors = colors; 730 current_colors = colors;
@@ -729,36 +732,69 @@ static void render_container_stacked(struct sway_output *output,
729 } 732 }
730 733
731 // Render surface and left/right/bottom borders 734 // Render surface and left/right/bottom borders
732 if (current->type == C_VIEW) { 735 if (current->view) {
733 render_view(output, damage, current, current_colors); 736 render_view(output, damage, current, current_colors);
734 } else { 737 } else {
735 render_container(output, damage, current, 738 render_container(output, damage, current,
736 parent_focused || current->current.focused); 739 parent->focused || current->current.focused);
737 } 740 }
738} 741}
739 742
740static void render_container(struct sway_output *output, 743static void render_containers(struct sway_output *output,
741 pixman_region32_t *damage, struct sway_container *con, 744 pixman_region32_t *damage, struct parent_data *parent) {
742 bool parent_focused) { 745 switch (parent->layout) {
743 switch (con->current.layout) {
744 case L_NONE: 746 case L_NONE:
745 case L_HORIZ: 747 case L_HORIZ:
746 case L_VERT: 748 case L_VERT:
747 render_container_simple(output, damage, con, parent_focused); 749 render_containers_linear(output, damage, parent);
748 break; 750 break;
749 case L_STACKED: 751 case L_STACKED:
750 render_container_stacked(output, damage, con, parent_focused); 752 render_containers_stacked(output, damage, parent);
751 break; 753 break;
752 case L_TABBED: 754 case L_TABBED:
753 render_container_tabbed(output, damage, con, parent_focused); 755 render_containers_tabbed(output, damage, parent);
754 break; 756 break;
755 } 757 }
756} 758}
757 759
760static void render_container(struct sway_output *output,
761 pixman_region32_t *damage, struct sway_container *con, bool focused) {
762 struct parent_data data = {
763 .layout = con->current.layout,
764 .box = {
765 .x = con->current.con_x,
766 .y = con->current.con_y,
767 .width = con->current.con_width,
768 .height = con->current.con_height,
769 },
770 .children = con->current.children,
771 .focused = focused,
772 .active_child = con->current.focused_inactive_child,
773 };
774 render_containers(output, damage, &data);
775}
776
777static void render_workspace(struct sway_output *output,
778 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
779 struct parent_data data = {
780 .layout = ws->current.layout,
781 .box = {
782 .x = ws->current.x,
783 .y = ws->current.y,
784 .width = ws->current.width,
785 .height = ws->current.height,
786 },
787 .children = ws->current.tiling,
788 .focused = focused,
789 .active_child = ws->current.focused_inactive_child,
790 };
791 render_containers(output, damage, &data);
792}
793
758static void render_floating_container(struct sway_output *soutput, 794static void render_floating_container(struct sway_output *soutput,
759 pixman_region32_t *damage, struct sway_container *con) { 795 pixman_region32_t *damage, struct sway_container *con) {
760 if (con->type == C_VIEW) { 796 if (con->view) {
761 struct sway_view *view = con->sway_view; 797 struct sway_view *view = con->view;
762 struct border_colors *colors; 798 struct border_colors *colors;
763 struct wlr_texture *title_texture; 799 struct wlr_texture *title_texture;
764 struct wlr_texture *marks_texture; 800 struct wlr_texture *marks_texture;
@@ -777,10 +813,10 @@ static void render_floating_container(struct sway_output *soutput,
777 marks_texture = view->marks_unfocused; 813 marks_texture = view->marks_unfocused;
778 } 814 }
779 815
780 if (!view->swayc->current.using_csd) { 816 if (!view->container->current.using_csd) {
781 if (con->current.border == B_NORMAL) { 817 if (con->current.border == B_NORMAL) {
782 render_titlebar(soutput, damage, con, con->current.swayc_x, 818 render_titlebar(soutput, damage, con, con->current.con_x,
783 con->current.swayc_y, con->current.swayc_width, colors, 819 con->current.con_y, con->current.con_width, colors,
784 title_texture, marks_texture); 820 title_texture, marks_texture);
785 } else if (con->current.border != B_NONE) { 821 } else if (con->current.border != B_NONE) {
786 render_top_border(soutput, damage, con, colors); 822 render_top_border(soutput, damage, con, colors);
@@ -794,17 +830,15 @@ static void render_floating_container(struct sway_output *soutput,
794 830
795static void render_floating(struct sway_output *soutput, 831static void render_floating(struct sway_output *soutput,
796 pixman_region32_t *damage) { 832 pixman_region32_t *damage) {
797 for (int i = 0; i < root_container.current.children->length; ++i) { 833 for (int i = 0; i < root->outputs->length; ++i) {
798 struct sway_container *output = 834 struct sway_output *output = root->outputs->items[i];
799 root_container.current.children->items[i]; 835 for (int j = 0; j < output->current.workspaces->length; ++j) {
800 for (int j = 0; j < output->current.children->length; ++j) { 836 struct sway_workspace *ws = output->current.workspaces->items[j];
801 struct sway_container *ws = output->current.children->items[j];
802 if (!workspace_is_visible(ws)) { 837 if (!workspace_is_visible(ws)) {
803 continue; 838 continue;
804 } 839 }
805 list_t *floating = ws->current.ws_floating; 840 for (int k = 0; k < ws->current.floating->length; ++k) {
806 for (int k = 0; k < floating->length; ++k) { 841 struct sway_container *floater = ws->current.floating->items[k];
807 struct sway_container *floater = floating->items[k];
808 render_floating_container(soutput, damage, floater); 842 render_floating_container(soutput, damage, floater);
809 } 843 }
810 } 844 }
@@ -837,8 +871,8 @@ void output_render(struct sway_output *output, struct timespec *when,
837 pixman_region32_union_rect(damage, damage, 0, 0, width, height); 871 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
838 } 872 }
839 873
840 struct sway_container *workspace = output_get_active_workspace(output); 874 struct sway_workspace *workspace = output_get_active_workspace(output);
841 struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; 875 struct sway_container *fullscreen_con = workspace->current.fullscreen;
842 876
843 if (output_has_opaque_overlay_layer_surface(output)) { 877 if (output_has_opaque_overlay_layer_surface(output)) {
844 goto render_overlay; 878 goto render_overlay;
@@ -855,12 +889,11 @@ void output_render(struct sway_output *output, struct timespec *when,
855 } 889 }
856 890
857 // TODO: handle views smaller than the output 891 // TODO: handle views smaller than the output
858 if (fullscreen_con->type == C_VIEW) { 892 if (fullscreen_con->view) {
859 if (fullscreen_con->sway_view->saved_buffer) { 893 if (fullscreen_con->view->saved_buffer) {
860 render_saved_view(fullscreen_con->sway_view, 894 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
861 output, damage, 1.0f);
862 } else { 895 } else {
863 render_view_toplevels(fullscreen_con->sway_view, 896 render_view_toplevels(fullscreen_con->view,
864 output, damage, 1.0f); 897 output, damage, 1.0f);
865 } 898 }
866 } else { 899 } else {
@@ -868,8 +901,7 @@ void output_render(struct sway_output *output, struct timespec *when,
868 fullscreen_con->current.focused); 901 fullscreen_con->current.focused);
869 } 902 }
870#ifdef HAVE_XWAYLAND 903#ifdef HAVE_XWAYLAND
871 render_unmanaged(output, damage, 904 render_unmanaged(output, damage, &root->xwayland_unmanaged);
872 &root_container.sway_root->xwayland_unmanaged);
873#endif 905#endif
874 } else { 906 } else {
875 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; 907 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
@@ -886,31 +918,30 @@ void output_render(struct sway_output *output, struct timespec *when,
886 render_layer(output, damage, 918 render_layer(output, damage,
887 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); 919 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
888 920
889 render_container(output, damage, workspace, workspace->current.focused); 921 render_workspace(output, damage, workspace, workspace->current.focused);
890 render_floating(output, damage); 922 render_floating(output, damage);
891#ifdef HAVE_XWAYLAND 923#ifdef HAVE_XWAYLAND
892 render_unmanaged(output, damage, 924 render_unmanaged(output, damage, &root->xwayland_unmanaged);
893 &root_container.sway_root->xwayland_unmanaged);
894#endif 925#endif
895 render_layer(output, damage, 926 render_layer(output, damage,
896 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 927 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
897 } 928 }
898 929
899 struct sway_seat *seat = input_manager_current_seat(input_manager); 930 struct sway_seat *seat = input_manager_current_seat(input_manager);
900 struct sway_container *focus = seat_get_focus(seat); 931 struct sway_container *focus = seat_get_focused_container(seat);
901 if (focus && focus->type == C_VIEW) { 932 if (focus && focus->view) {
902 render_view_popups(focus->sway_view, output, damage, focus->alpha); 933 render_view_popups(focus->view, output, damage, focus->alpha);
903 } 934 }
904 935
905render_overlay: 936render_overlay:
906 render_layer(output, damage, 937 render_layer(output, damage,
907 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); 938 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
908 render_drag_icons(output, damage, &root_container.sway_root->drag_icons); 939 render_drag_icons(output, damage, &root->drag_icons);
909 940
910renderer_end: 941renderer_end:
911 if (debug.render_tree) { 942 if (debug.render_tree) {
912 wlr_renderer_scissor(renderer, NULL); 943 wlr_renderer_scissor(renderer, NULL);
913 wlr_render_texture(renderer, root_container.sway_root->debug_tree, 944 wlr_render_texture(renderer, root->debug_tree,
914 wlr_output->transform_matrix, 0, 40, 1); 945 wlr_output->transform_matrix, 0, 40, 1);
915 } 946 }
916 if (debug.damage == DAMAGE_HIGHLIGHT) { 947 if (debug.damage == DAMAGE_HIGHLIGHT) {
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 145c5f92..b4eec933 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -12,6 +12,7 @@
12#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
13#include "sway/output.h" 13#include "sway/output.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"
@@ -27,8 +28,12 @@ struct sway_transaction {
27 28
28struct sway_transaction_instruction { 29struct sway_transaction_instruction {
29 struct sway_transaction *transaction; 30 struct sway_transaction *transaction;
30 struct sway_container *container; 31 struct sway_node *node;
31 struct sway_container_state state; 32 union {
33 struct sway_output_state *output_state;
34 struct sway_workspace_state *workspace_state;
35 struct sway_container_state *container_state;
36 };
32 uint32_t serial; 37 uint32_t serial;
33}; 38};
34 39
@@ -47,26 +52,24 @@ static void transaction_destroy(struct sway_transaction *transaction) {
47 for (int i = 0; i < transaction->instructions->length; ++i) { 52 for (int i = 0; i < transaction->instructions->length; ++i) {
48 struct sway_transaction_instruction *instruction = 53 struct sway_transaction_instruction *instruction =
49 transaction->instructions->items[i]; 54 transaction->instructions->items[i];
50 struct sway_container *con = instruction->container; 55 struct sway_node *node = instruction->node;
51 con->ntxnrefs--; 56 node->ntxnrefs--;
52 if (con->instruction == instruction) { 57 if (node->instruction == instruction) {
53 con->instruction = NULL; 58 node->instruction = NULL;
54 } 59 }
55 if (con->destroying && con->ntxnrefs == 0) { 60 if (node->destroying && node->ntxnrefs == 0) {
56 switch (con->type) { 61 switch (node->type) {
57 case C_ROOT: 62 case N_ROOT:
63 sway_assert(false, "Never reached");
58 break; 64 break;
59 case C_OUTPUT: 65 case N_OUTPUT:
60 output_destroy(con); 66 output_destroy(node->sway_output);
61 break; 67 break;
62 case C_WORKSPACE: 68 case N_WORKSPACE:
63 workspace_destroy(con); 69 workspace_destroy(node->sway_workspace);
64 break; 70 break;
65 case C_CONTAINER: 71 case N_CONTAINER:
66 case C_VIEW: 72 container_destroy(node->sway_container);
67 container_destroy(con);
68 break;
69 case C_TYPES:
70 break; 73 break;
71 } 74 }
72 } 75 }
@@ -80,22 +83,79 @@ static void transaction_destroy(struct sway_transaction *transaction) {
80 free(transaction); 83 free(transaction);
81} 84}
82 85
83static void copy_pending_state(struct sway_container *container, 86static void copy_output_state(struct sway_output *output,
84 struct sway_container_state *state) { 87 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state =
89 calloc(1, sizeof(struct sway_output_state));
90 if (!state) {
91 wlr_log(WLR_ERROR, "Could not allocate output state");
92 return;
93 }
94 instruction->output_state = state;
95
96 state->workspaces = create_list();
97 list_cat(state->workspaces, output->workspaces);
98
99 state->active_workspace = output_get_active_workspace(output);
100}
101
102static void copy_workspace_state(struct sway_workspace *ws,
103 struct sway_transaction_instruction *instruction) {
104 struct sway_workspace_state *state =
105 calloc(1, sizeof(struct sway_workspace_state));
106 if (!state) {
107 wlr_log(WLR_ERROR, "Could not allocate workspace state");
108 return;
109 }
110 instruction->workspace_state = state;
111
112 state->fullscreen = ws->fullscreen;
113 state->x = ws->x;
114 state->y = ws->y;
115 state->width = ws->width;
116 state->height = ws->height;
117 state->layout = ws->layout;
118
119 state->output = ws->output;
120 state->floating = create_list();
121 state->tiling = create_list();
122 list_cat(state->floating, ws->floating);
123 list_cat(state->tiling, ws->tiling);
124
125 struct sway_seat *seat = input_manager_current_seat(input_manager);
126 state->focused = seat_get_focus(seat) == &ws->node;
127
128 // Set focused_inactive_child to the direct tiling child
129 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws);
130 if (focus) {
131 while (focus->parent) {
132 focus = focus->parent;
133 }
134 }
135 state->focused_inactive_child = focus;
136}
137
138static void copy_container_state(struct sway_container *container,
139 struct sway_transaction_instruction *instruction) {
140 struct sway_container_state *state =
141 calloc(1, sizeof(struct sway_container_state));
142 if (!state) {
143 wlr_log(WLR_ERROR, "Could not allocate container state");
144 return;
145 }
146 instruction->container_state = state;
147
85 state->layout = container->layout; 148 state->layout = container->layout;
86 state->swayc_x = container->x; 149 state->con_x = container->x;
87 state->swayc_y = container->y; 150 state->con_y = container->y;
88 state->swayc_width = container->width; 151 state->con_width = container->width;
89 state->swayc_height = container->height; 152 state->con_height = container->height;
90 state->is_fullscreen = container->is_fullscreen; 153 state->is_fullscreen = container->is_fullscreen;
91 state->has_gaps = container->has_gaps;
92 state->current_gaps = container->current_gaps;
93 state->gaps_inner = container->gaps_inner;
94 state->gaps_outer = container->gaps_outer;
95 state->parent = container->parent; 154 state->parent = container->parent;
155 state->workspace = container->workspace;
96 156
97 if (container->type == C_VIEW) { 157 if (container->view) {
98 struct sway_view *view = container->sway_view; 158 struct sway_view *view = container->view;
99 state->view_x = view->x; 159 state->view_x = view->x;
100 state->view_y = view->y; 160 state->view_y = view->y;
101 state->view_width = view->width; 161 state->view_width = view->width;
@@ -107,50 +167,111 @@ static void copy_pending_state(struct sway_container *container,
107 state->border_right = view->border_right; 167 state->border_right = view->border_right;
108 state->border_bottom = view->border_bottom; 168 state->border_bottom = view->border_bottom;
109 state->using_csd = view->using_csd; 169 state->using_csd = view->using_csd;
110 } else if (container->type == C_WORKSPACE) {
111 state->ws_fullscreen = container->sway_workspace->fullscreen;
112 state->ws_floating = create_list();
113 state->children = create_list();
114 list_cat(state->ws_floating, container->sway_workspace->floating);
115 list_cat(state->children, container->children);
116 } else { 170 } else {
117 state->children = create_list(); 171 state->children = create_list();
118 list_cat(state->children, container->children); 172 list_cat(state->children, container->children);
119 } 173 }
120 174
121 struct sway_seat *seat = input_manager_current_seat(input_manager); 175 struct sway_seat *seat = input_manager_current_seat(input_manager);
122 state->focused = seat_get_focus(seat) == container; 176 state->focused = seat_get_focus(seat) == &container->node;
123 177
124 if (container->type == C_WORKSPACE) { 178 if (!container->view) {
125 // Set focused_inactive_child to the direct tiling child 179 struct sway_node *focus = seat_get_active_child(seat, &container->node);
126 struct sway_container *focus = 180 state->focused_inactive_child = focus ? focus->sway_container : NULL;
127 seat_get_focus_inactive_tiling(seat, container);
128 if (focus && focus->type > C_WORKSPACE) {
129 while (focus->parent->type != C_WORKSPACE) {
130 focus = focus->parent;
131 }
132 }
133 state->focused_inactive_child = focus;
134 } else if (container->type != C_VIEW) {
135 state->focused_inactive_child =
136 seat_get_active_child(seat, container);
137 } 181 }
138} 182}
139 183
140static void transaction_add_container(struct sway_transaction *transaction, 184static void transaction_add_node(struct sway_transaction *transaction,
141 struct sway_container *container) { 185 struct sway_node *node) {
142 struct sway_transaction_instruction *instruction = 186 struct sway_transaction_instruction *instruction =
143 calloc(1, sizeof(struct sway_transaction_instruction)); 187 calloc(1, sizeof(struct sway_transaction_instruction));
144 if (!sway_assert(instruction, "Unable to allocate instruction")) { 188 if (!sway_assert(instruction, "Unable to allocate instruction")) {
145 return; 189 return;
146 } 190 }
147 instruction->transaction = transaction; 191 instruction->transaction = transaction;
148 instruction->container = container; 192 instruction->node = node;
149 193
150 copy_pending_state(container, &instruction->state); 194 switch (node->type) {
195 case N_ROOT:
196 break;
197 case N_OUTPUT:
198 copy_output_state(node->sway_output, instruction);
199 break;
200 case N_WORKSPACE:
201 copy_workspace_state(node->sway_workspace, instruction);
202 break;
203 case N_CONTAINER:
204 copy_container_state(node->sway_container, instruction);
205 break;
206 }
151 207
152 list_add(transaction->instructions, instruction); 208 list_add(transaction->instructions, instruction);
153 container->ntxnrefs++; 209 node->ntxnrefs++;
210}
211
212static void apply_output_state(struct sway_output *output,
213 struct sway_output_state *state) {
214 output_damage_whole(output);
215 list_free(output->current.workspaces);
216 memcpy(&output->current, state, sizeof(struct sway_output_state));
217 output_damage_whole(output);
218}
219
220static void apply_workspace_state(struct sway_workspace *ws,
221 struct sway_workspace_state *state) {
222 output_damage_whole(ws->current.output);
223 list_free(ws->current.floating);
224 list_free(ws->current.tiling);
225 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
226 output_damage_whole(ws->current.output);
227}
228
229static void apply_container_state(struct sway_container *container,
230 struct sway_container_state *state) {
231 struct sway_view *view = container->view;
232 // Damage the old location
233 desktop_damage_whole_container(container);
234 if (view && view->saved_buffer) {
235 struct wlr_box box = {
236 .x = container->current.view_x - view->saved_geometry.x,
237 .y = container->current.view_y - view->saved_geometry.y,
238 .width = view->saved_buffer_width,
239 .height = view->saved_buffer_height,
240 };
241 desktop_damage_box(&box);
242 }
243
244 // There are separate children lists for each instruction state, the
245 // container's current state and the container's pending state
246 // (ie. con->children). The list itself needs to be freed here.
247 // Any child containers which are being deleted will be cleaned up in
248 // transaction_destroy().
249 list_free(container->current.children);
250
251 memcpy(&container->current, state, sizeof(struct sway_container_state));
252
253 if (view && view->saved_buffer) {
254 if (!container->node.destroying || container->node.ntxnrefs == 1) {
255 view_remove_saved_buffer(view);
256 }
257 }
258
259 // Damage the new location
260 desktop_damage_whole_container(container);
261 if (view && view->surface) {
262 struct wlr_surface *surface = view->surface;
263 struct wlr_box box = {
264 .x = container->current.view_x - view->geometry.x,
265 .y = container->current.view_y - view->geometry.y,
266 .width = surface->current.width,
267 .height = surface->current.height,
268 };
269 desktop_damage_box(&box);
270 }
271
272 if (!container->node.destroying) {
273 container_discover_outputs(container);
274 }
154} 275}
155 276
156/** 277/**
@@ -168,67 +289,36 @@ static void transaction_apply(struct sway_transaction *transaction) {
168 "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60)); 289 "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60));
169 } 290 }
170 291
171 // Apply the instruction state to the container's current state 292 // Apply the instruction state to the node's current state
172 for (int i = 0; i < transaction->instructions->length; ++i) { 293 for (int i = 0; i < transaction->instructions->length; ++i) {
173 struct sway_transaction_instruction *instruction = 294 struct sway_transaction_instruction *instruction =
174 transaction->instructions->items[i]; 295 transaction->instructions->items[i];
175 struct sway_container *container = instruction->container; 296 struct sway_node *node = instruction->node;
176
177 // Damage the old location
178 desktop_damage_whole_container(container);
179 if (container->type == C_VIEW && container->sway_view->saved_buffer) {
180 struct sway_view *view = container->sway_view;
181 struct wlr_box box = {
182 .x = container->current.view_x - view->saved_geometry.x,
183 .y = container->current.view_y - view->saved_geometry.y,
184 .width = view->saved_buffer_width,
185 .height = view->saved_buffer_height,
186 };
187 desktop_damage_box(&box);
188 }
189
190 // There are separate children lists for each instruction state, the
191 // container's current state and the container's pending state
192 // (ie. con->children). The list itself needs to be freed here.
193 // Any child containers which are being deleted will be cleaned up in
194 // transaction_destroy().
195 list_free(container->current.children);
196 list_free(container->current.ws_floating);
197
198 memcpy(&container->current, &instruction->state,
199 sizeof(struct sway_container_state));
200
201 if (container->type == C_VIEW && container->sway_view->saved_buffer) {
202 if (!container->destroying || container->ntxnrefs == 1) {
203 view_remove_saved_buffer(container->sway_view);
204 }
205 }
206 297
207 // Damage the new location 298 switch (node->type) {
208 desktop_damage_whole_container(container); 299 case N_ROOT:
209 if (container->type == C_VIEW && container->sway_view->surface) { 300 break;
210 struct sway_view *view = container->sway_view; 301 case N_OUTPUT:
211 struct wlr_surface *surface = view->surface; 302 apply_output_state(node->sway_output, instruction->output_state);
212 struct wlr_box box = { 303 break;
213 .x = container->current.view_x - view->geometry.x, 304 case N_WORKSPACE:
214 .y = container->current.view_y - view->geometry.y, 305 apply_workspace_state(node->sway_workspace,
215 .width = surface->current.width, 306 instruction->workspace_state);
216 .height = surface->current.height, 307 break;
217 }; 308 case N_CONTAINER:
218 desktop_damage_box(&box); 309 apply_container_state(node->sway_container,
310 instruction->container_state);
311 break;
219 } 312 }
220 313
221 container->instruction = NULL; 314 node->instruction = NULL;
222 if (container->type == C_CONTAINER || container->type == C_VIEW) {
223 container_discover_outputs(container);
224 }
225 } 315 }
226} 316}
227 317
228static void transaction_commit(struct sway_transaction *transaction); 318static void transaction_commit(struct sway_transaction *transaction);
229 319
230// Return true if both transactions operate on the same containers 320// Return true if both transactions operate on the same nodes
231static bool transaction_same_containers(struct sway_transaction *a, 321static bool transaction_same_nodes(struct sway_transaction *a,
232 struct sway_transaction *b) { 322 struct sway_transaction *b) {
233 if (a->instructions->length != b->instructions->length) { 323 if (a->instructions->length != b->instructions->length) {
234 return false; 324 return false;
@@ -236,7 +326,7 @@ static bool transaction_same_containers(struct sway_transaction *a,
236 for (int i = 0; i < a->instructions->length; ++i) { 326 for (int i = 0; i < a->instructions->length; ++i) {
237 struct sway_transaction_instruction *a_inst = a->instructions->items[i]; 327 struct sway_transaction_instruction *a_inst = a->instructions->items[i];
238 struct sway_transaction_instruction *b_inst = b->instructions->items[i]; 328 struct sway_transaction_instruction *b_inst = b->instructions->items[i];
239 if (a_inst->container != b_inst->container) { 329 if (a_inst->node != b_inst->node) {
240 return false; 330 return false;
241 } 331 }
242 } 332 }
@@ -267,7 +357,7 @@ static void transaction_progress_queue() {
267 while (server.transactions->length >= 2) { 357 while (server.transactions->length >= 2) {
268 struct sway_transaction *a = server.transactions->items[0]; 358 struct sway_transaction *a = server.transactions->items[0];
269 struct sway_transaction *b = server.transactions->items[1]; 359 struct sway_transaction *b = server.transactions->items[1];
270 if (transaction_same_containers(a, b)) { 360 if (transaction_same_nodes(a, b)) {
271 list_del(server.transactions, 0); 361 list_del(server.transactions, 0);
272 transaction_destroy(a); 362 transaction_destroy(a);
273 } else { 363 } else {
@@ -289,16 +379,18 @@ static int handle_timeout(void *data) {
289 return 0; 379 return 0;
290} 380}
291 381
292static bool should_configure(struct sway_container *con, 382static bool should_configure(struct sway_node *node,
293 struct sway_transaction_instruction *instruction) { 383 struct sway_transaction_instruction *instruction) {
294 if (con->type != C_VIEW) { 384 if (!node_is_view(node)) {
295 return false; 385 return false;
296 } 386 }
297 if (con->destroying) { 387 if (node->destroying) {
298 return false; 388 return false;
299 } 389 }
300 if (con->current.view_width == instruction->state.view_width && 390 struct sway_container_state *cstate = &node->sway_container->current;
301 con->current.view_height == instruction->state.view_height) { 391 struct sway_container_state *istate = instruction->container_state;
392 if (cstate->view_width == istate->view_width &&
393 cstate->view_height == istate->view_height) {
302 return false; 394 return false;
303 } 395 }
304 return true; 396 return true;
@@ -311,13 +403,13 @@ static void transaction_commit(struct sway_transaction *transaction) {
311 for (int i = 0; i < transaction->instructions->length; ++i) { 403 for (int i = 0; i < transaction->instructions->length; ++i) {
312 struct sway_transaction_instruction *instruction = 404 struct sway_transaction_instruction *instruction =
313 transaction->instructions->items[i]; 405 transaction->instructions->items[i];
314 struct sway_container *con = instruction->container; 406 struct sway_node *node = instruction->node;
315 if (should_configure(con, instruction)) { 407 if (should_configure(node, instruction)) {
316 instruction->serial = view_configure(con->sway_view, 408 instruction->serial = view_configure(node->sway_container->view,
317 instruction->state.view_x, 409 instruction->container_state->view_x,
318 instruction->state.view_y, 410 instruction->container_state->view_y,
319 instruction->state.view_width, 411 instruction->container_state->view_width,
320 instruction->state.view_height); 412 instruction->container_state->view_height);
321 ++transaction->num_waiting; 413 ++transaction->num_waiting;
322 414
323 // From here on we are rendering a saved buffer of the view, which 415 // From here on we are rendering a saved buffer of the view, which
@@ -325,14 +417,16 @@ static void transaction_commit(struct sway_transaction *transaction) {
325 // as soon as possible. Additionally, this is required if a view is 417 // as soon as possible. Additionally, this is required if a view is
326 // mapping and its default geometry doesn't intersect an output. 418 // mapping and its default geometry doesn't intersect an output.
327 struct timespec when; 419 struct timespec when;
328 wlr_surface_send_frame_done(con->sway_view->surface, &when); 420 wlr_surface_send_frame_done(
421 node->sway_container->view->surface, &when);
329 } 422 }
330 if (con->type == C_VIEW && !con->sway_view->saved_buffer) { 423 if (node_is_view(node) && !node->sway_container->view->saved_buffer) {
331 view_save_buffer(con->sway_view); 424 view_save_buffer(node->sway_container->view);
332 memcpy(&con->sway_view->saved_geometry, &con->sway_view->geometry, 425 memcpy(&node->sway_container->view->saved_geometry,
426 &node->sway_container->view->geometry,
333 sizeof(struct wlr_box)); 427 sizeof(struct wlr_box));
334 } 428 }
335 con->instruction = instruction; 429 node->instruction = instruction;
336 } 430 }
337 transaction->num_configures = transaction->num_waiting; 431 transaction->num_configures = transaction->num_waiting;
338 if (debug.txn_timings) { 432 if (debug.txn_timings) {
@@ -381,7 +475,7 @@ static void set_instruction_ready(
381 transaction, 475 transaction,
382 transaction->num_configures - transaction->num_waiting + 1, 476 transaction->num_configures - transaction->num_waiting + 1,
383 transaction->num_configures, ms, 477 transaction->num_configures, ms,
384 instruction->container->name); 478 instruction->node->sway_container->title);
385 } 479 }
386 480
387 // If the transaction has timed out then its num_waiting will be 0 already. 481 // If the transaction has timed out then its num_waiting will be 0 already.
@@ -390,41 +484,43 @@ static void set_instruction_ready(
390 wl_event_source_timer_update(transaction->timer, 0); 484 wl_event_source_timer_update(transaction->timer, 0);
391 } 485 }
392 486
393 instruction->container->instruction = NULL; 487 instruction->node->instruction = NULL;
394 transaction_progress_queue(); 488 transaction_progress_queue();
395} 489}
396 490
397void transaction_notify_view_ready_by_serial(struct sway_view *view, 491void transaction_notify_view_ready_by_serial(struct sway_view *view,
398 uint32_t serial) { 492 uint32_t serial) {
399 struct sway_transaction_instruction *instruction = view->swayc->instruction; 493 struct sway_transaction_instruction *instruction =
400 if (view->swayc->instruction->serial == serial) { 494 view->container->node.instruction;
495 if (instruction->serial == serial) {
401 set_instruction_ready(instruction); 496 set_instruction_ready(instruction);
402 } 497 }
403} 498}
404 499
405void transaction_notify_view_ready_by_size(struct sway_view *view, 500void transaction_notify_view_ready_by_size(struct sway_view *view,
406 int width, int height) { 501 int width, int height) {
407 struct sway_transaction_instruction *instruction = view->swayc->instruction; 502 struct sway_transaction_instruction *instruction =
408 if (instruction->state.view_width == width && 503 view->container->node.instruction;
409 instruction->state.view_height == height) { 504 if (instruction->container_state->view_width == width &&
505 instruction->container_state->view_height == height) {
410 set_instruction_ready(instruction); 506 set_instruction_ready(instruction);
411 } 507 }
412} 508}
413 509
414void transaction_commit_dirty(void) { 510void transaction_commit_dirty(void) {
415 if (!server.dirty_containers->length) { 511 if (!server.dirty_nodes->length) {
416 return; 512 return;
417 } 513 }
418 struct sway_transaction *transaction = transaction_create(); 514 struct sway_transaction *transaction = transaction_create();
419 if (!transaction) { 515 if (!transaction) {
420 return; 516 return;
421 } 517 }
422 for (int i = 0; i < server.dirty_containers->length; ++i) { 518 for (int i = 0; i < server.dirty_nodes->length; ++i) {
423 struct sway_container *container = server.dirty_containers->items[i]; 519 struct sway_node *node = server.dirty_nodes->items[i];
424 transaction_add_container(transaction, container); 520 transaction_add_node(transaction, node);
425 container->dirty = false; 521 node->dirty = false;
426 } 522 }
427 server.dirty_containers->length = 0; 523 server.dirty_nodes->length = 0;
428 524
429 list_add(server.transactions, transaction); 525 list_add(server.transactions, transaction);
430 526
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 587deb0f..e19d8e18 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -11,10 +11,12 @@
11#include "sway/desktop/transaction.h" 11#include "sway/desktop/transaction.h"
12#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
13#include "sway/input/seat.h" 13#include "sway/input/seat.h"
14#include "sway/output.h"
14#include "sway/server.h" 15#include "sway/server.h"
15#include "sway/tree/arrange.h" 16#include "sway/tree/arrange.h"
16#include "sway/tree/container.h" 17#include "sway/tree/container.h"
17#include "sway/tree/view.h" 18#include "sway/tree/view.h"
19#include "sway/tree/workspace.h"
18 20
19static const struct sway_view_child_impl popup_impl; 21static const struct sway_view_child_impl popup_impl;
20 22
@@ -52,15 +54,15 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
52 struct sway_view *view = popup->child.view; 54 struct sway_view *view = popup->child.view;
53 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 55 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
54 56
55 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 57 struct sway_output *output = view->container->workspace->output;
56 58
57 // the output box expressed in the coordinate system of the toplevel parent 59 // the output box expressed in the coordinate system of the toplevel parent
58 // of the popup 60 // of the popup
59 struct wlr_box output_toplevel_sx_box = { 61 struct wlr_box output_toplevel_sx_box = {
60 .x = output->x - view->x, 62 .x = output->wlr_output->lx - view->x,
61 .y = output->y - view->y, 63 .y = output->wlr_output->ly - view->y,
62 .width = output->width, 64 .width = output->wlr_output->width,
63 .height = output->height, 65 .height = output->wlr_output->height,
64 }; 66 };
65 67
66 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 68 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@@ -252,11 +254,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
252 struct sway_view *view = &xdg_shell_view->view; 254 struct sway_view *view = &xdg_shell_view->view;
253 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 255 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
254 256
255 if (!view->swayc) { 257 if (view->container->node.instruction) {
256 return;
257 }
258
259 if (view->swayc->instruction) {
260 wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); 258 wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry);
261 transaction_notify_view_ready_by_serial(view, 259 transaction_notify_view_ready_by_serial(view,
262 xdg_surface->configure_serial); 260 xdg_surface->configure_serial);
@@ -265,7 +263,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
265 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 263 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
266 264
267 if ((new_geo.width != view->width || new_geo.height != view->height) && 265 if ((new_geo.width != view->width || new_geo.height != view->height) &&
268 container_is_floating(view->swayc)) { 266 container_is_floating(view->container)) {
269 // A floating view has unexpectedly sent a new size 267 // A floating view has unexpectedly sent a new size
270 desktop_damage_view(view); 268 desktop_damage_view(view);
271 view_update_size(view, new_geo.width, new_geo.height); 269 view_update_size(view, new_geo.width, new_geo.height);
@@ -319,10 +317,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
319 return; 317 return;
320 } 318 }
321 319
322 container_set_fullscreen(view->swayc, e->fullscreen); 320 container_set_fullscreen(view->container, e->fullscreen);
323 321
324 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 322 arrange_workspace(view->container->workspace);
325 arrange_windows(output);
326 transaction_commit_dirty(); 323 transaction_commit_dirty();
327} 324}
328 325
@@ -330,13 +327,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
330 struct sway_xdg_shell_view *xdg_shell_view = 327 struct sway_xdg_shell_view *xdg_shell_view =
331 wl_container_of(listener, xdg_shell_view, request_move); 328 wl_container_of(listener, xdg_shell_view, request_move);
332 struct sway_view *view = &xdg_shell_view->view; 329 struct sway_view *view = &xdg_shell_view->view;
333 if (!container_is_floating(view->swayc)) { 330 if (!container_is_floating(view->container)) {
334 return; 331 return;
335 } 332 }
336 struct wlr_xdg_toplevel_move_event *e = data; 333 struct wlr_xdg_toplevel_move_event *e = data;
337 struct sway_seat *seat = e->seat->seat->data; 334 struct sway_seat *seat = e->seat->seat->data;
338 if (e->serial == seat->last_button_serial) { 335 if (e->serial == seat->last_button_serial) {
339 seat_begin_move(seat, view->swayc, seat->last_button); 336 seat_begin_move(seat, view->container, seat->last_button);
340 } 337 }
341} 338}
342 339
@@ -344,13 +341,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
344 struct sway_xdg_shell_view *xdg_shell_view = 341 struct sway_xdg_shell_view *xdg_shell_view =
345 wl_container_of(listener, xdg_shell_view, request_resize); 342 wl_container_of(listener, xdg_shell_view, request_resize);
346 struct sway_view *view = &xdg_shell_view->view; 343 struct sway_view *view = &xdg_shell_view->view;
347 if (!container_is_floating(view->swayc)) { 344 if (!container_is_floating(view->container)) {
348 return; 345 return;
349 } 346 }
350 struct wlr_xdg_toplevel_resize_event *e = data; 347 struct wlr_xdg_toplevel_resize_event *e = data;
351 struct sway_seat *seat = e->seat->seat->data; 348 struct sway_seat *seat = e->seat->seat->data;
352 if (e->serial == seat->last_button_serial) { 349 if (e->serial == seat->last_button_serial) {
353 seat_begin_resize_floating(seat, view->swayc, 350 seat_begin_resize_floating(seat, view->container,
354 seat->last_button, e->edges); 351 seat->last_button, e->edges);
355 } 352 }
356} 353}
@@ -399,11 +396,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
399 view_map(view, view->wlr_xdg_surface->surface); 396 view_map(view, view->wlr_xdg_surface->surface);
400 397
401 if (xdg_surface->toplevel->client_pending.fullscreen) { 398 if (xdg_surface->toplevel->client_pending.fullscreen) {
402 container_set_fullscreen(view->swayc, true); 399 container_set_fullscreen(view->container, true);
403 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 400 arrange_workspace(view->container->workspace);
404 arrange_windows(ws);
405 } else { 401 } else {
406 arrange_windows(view->swayc->parent); 402 if (view->container->parent) {
403 arrange_container(view->container->parent);
404 } else {
405 arrange_workspace(view->container->workspace);
406 }
407 } 407 }
408 transaction_commit_dirty(); 408 transaction_commit_dirty();
409 409
@@ -440,8 +440,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
440 struct sway_xdg_shell_view *xdg_shell_view = 440 struct sway_xdg_shell_view *xdg_shell_view =
441 wl_container_of(listener, xdg_shell_view, destroy); 441 wl_container_of(listener, xdg_shell_view, destroy);
442 struct sway_view *view = &xdg_shell_view->view; 442 struct sway_view *view = &xdg_shell_view->view;
443 if (!sway_assert(view->swayc == NULL || view->swayc->destroying, 443 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
444 "Tried to destroy a mapped view")) {
445 return; 444 return;
446 } 445 }
447 wl_list_remove(&xdg_shell_view->destroy.link); 446 wl_list_remove(&xdg_shell_view->destroy.link);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 175416f3..b23d4577 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -10,10 +10,12 @@
10#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
11#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
12#include "sway/input/seat.h" 12#include "sway/input/seat.h"
13#include "sway/output.h"
13#include "sway/server.h" 14#include "sway/server.h"
14#include "sway/tree/arrange.h" 15#include "sway/tree/arrange.h"
15#include "sway/tree/container.h" 16#include "sway/tree/container.h"
16#include "sway/tree/view.h" 17#include "sway/tree/view.h"
18#include "sway/tree/workspace.h"
17 19
18static const struct sway_view_child_impl popup_impl; 20static const struct sway_view_child_impl popup_impl;
19 21
@@ -51,15 +53,15 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
51 struct sway_view *view = popup->child.view; 53 struct sway_view *view = popup->child.view;
52 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; 54 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
53 55
54 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 56 struct sway_output *output = view->container->workspace->output;
55 57
56 // the output box expressed in the coordinate system of the toplevel parent 58 // the output box expressed in the coordinate system of the toplevel parent
57 // of the popup 59 // of the popup
58 struct wlr_box output_toplevel_sx_box = { 60 struct wlr_box output_toplevel_sx_box = {
59 .x = output->x - view->x, 61 .x = output->wlr_output->lx - view->x,
60 .y = output->y - view->y, 62 .y = output->wlr_output->ly - view->y,
61 .width = output->width, 63 .width = output->wlr_output->width,
62 .height = output->height, 64 .height = output->wlr_output->height,
63 }; 65 };
64 66
65 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 67 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@@ -249,11 +251,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
249 struct sway_view *view = &xdg_shell_v6_view->view; 251 struct sway_view *view = &xdg_shell_v6_view->view;
250 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; 252 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
251 253
252 if (!view->swayc) { 254 if (view->container->node.instruction) {
253 return;
254 }
255
256 if (view->swayc->instruction) {
257 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); 255 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry);
258 transaction_notify_view_ready_by_serial(view, 256 transaction_notify_view_ready_by_serial(view,
259 xdg_surface_v6->configure_serial); 257 xdg_surface_v6->configure_serial);
@@ -262,7 +260,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
262 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); 260 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo);
263 261
264 if ((new_geo.width != view->width || new_geo.height != view->height) && 262 if ((new_geo.width != view->width || new_geo.height != view->height) &&
265 container_is_floating(view->swayc)) { 263 container_is_floating(view->container)) {
266 // A floating view has unexpectedly sent a new size 264 // A floating view has unexpectedly sent a new size
267 desktop_damage_view(view); 265 desktop_damage_view(view);
268 view_update_size(view, new_geo.width, new_geo.height); 266 view_update_size(view, new_geo.width, new_geo.height);
@@ -316,10 +314,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
316 return; 314 return;
317 } 315 }
318 316
319 container_set_fullscreen(view->swayc, e->fullscreen); 317 container_set_fullscreen(view->container, e->fullscreen);
320 318
321 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 319 arrange_workspace(view->container->workspace);
322 arrange_windows(output);
323 transaction_commit_dirty(); 320 transaction_commit_dirty();
324} 321}
325 322
@@ -327,13 +324,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
327 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 324 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
328 wl_container_of(listener, xdg_shell_v6_view, request_move); 325 wl_container_of(listener, xdg_shell_v6_view, request_move);
329 struct sway_view *view = &xdg_shell_v6_view->view; 326 struct sway_view *view = &xdg_shell_v6_view->view;
330 if (!container_is_floating(view->swayc)) { 327 if (!container_is_floating(view->container)) {
331 return; 328 return;
332 } 329 }
333 struct wlr_xdg_toplevel_v6_move_event *e = data; 330 struct wlr_xdg_toplevel_v6_move_event *e = data;
334 struct sway_seat *seat = e->seat->seat->data; 331 struct sway_seat *seat = e->seat->seat->data;
335 if (e->serial == seat->last_button_serial) { 332 if (e->serial == seat->last_button_serial) {
336 seat_begin_move(seat, view->swayc, seat->last_button); 333 seat_begin_move(seat, view->container, seat->last_button);
337 } 334 }
338} 335}
339 336
@@ -341,13 +338,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
341 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 338 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
342 wl_container_of(listener, xdg_shell_v6_view, request_resize); 339 wl_container_of(listener, xdg_shell_v6_view, request_resize);
343 struct sway_view *view = &xdg_shell_v6_view->view; 340 struct sway_view *view = &xdg_shell_v6_view->view;
344 if (!container_is_floating(view->swayc)) { 341 if (!container_is_floating(view->container)) {
345 return; 342 return;
346 } 343 }
347 struct wlr_xdg_toplevel_v6_resize_event *e = data; 344 struct wlr_xdg_toplevel_v6_resize_event *e = data;
348 struct sway_seat *seat = e->seat->seat->data; 345 struct sway_seat *seat = e->seat->seat->data;
349 if (e->serial == seat->last_button_serial) { 346 if (e->serial == seat->last_button_serial) {
350 seat_begin_resize_floating(seat, view->swayc, 347 seat_begin_resize_floating(seat, view->container,
351 seat->last_button, e->edges); 348 seat->last_button, e->edges);
352 } 349 }
353} 350}
@@ -396,11 +393,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
396 view_map(view, view->wlr_xdg_surface_v6->surface); 393 view_map(view, view->wlr_xdg_surface_v6->surface);
397 394
398 if (xdg_surface->toplevel->client_pending.fullscreen) { 395 if (xdg_surface->toplevel->client_pending.fullscreen) {
399 container_set_fullscreen(view->swayc, true); 396 container_set_fullscreen(view->container, true);
400 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 397 arrange_workspace(view->container->workspace);
401 arrange_windows(ws);
402 } else { 398 } else {
403 arrange_windows(view->swayc->parent); 399 if (view->container->parent) {
400 arrange_container(view->container->parent);
401 } else {
402 arrange_workspace(view->container->workspace);
403 }
404 } 404 }
405 transaction_commit_dirty(); 405 transaction_commit_dirty();
406 406
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 94a30239..0d192b76 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -59,8 +59,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
59 wl_container_of(listener, surface, map); 59 wl_container_of(listener, surface, map);
60 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 60 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
61 61
62 wl_list_insert(root_container.sway_root->xwayland_unmanaged.prev, 62 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link);
63 &surface->link);
64 63
65 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 64 wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
66 surface->commit.notify = unmanaged_handle_commit; 65 surface->commit.notify = unmanaged_handle_commit;
@@ -90,11 +89,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
90 if (seat->wlr_seat->keyboard_state.focused_surface == 89 if (seat->wlr_seat->keyboard_state.focused_surface ==
91 xsurface->surface) { 90 xsurface->surface) {
92 // Restore focus 91 // Restore focus
93 struct sway_container *previous = 92 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
94 seat_get_focus_inactive(seat, &root_container);
95 if (previous) { 93 if (previous) {
96 // Hack to get seat to re-focus the return value of get_focus 94 // Hack to get seat to re-focus the return value of get_focus
97 seat_set_focus(seat, previous->parent); 95 seat_set_focus(seat, NULL);
98 seat_set_focus(seat, previous); 96 seat_set_focus(seat, previous);
99 } 97 }
100 } 98 }
@@ -299,7 +297,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
299 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 297 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
300 struct wlr_surface_state *state = &xsurface->surface->current; 298 struct wlr_surface_state *state = &xsurface->surface->current;
301 299
302 if (view->swayc->instruction) { 300 if (view->container->node.instruction) {
303 get_geometry(view, &view->geometry); 301 get_geometry(view, &view->geometry);
304 transaction_notify_view_ready_by_size(view, 302 transaction_notify_view_ready_by_size(view,
305 state->width, state->height); 303 state->width, state->height);
@@ -308,7 +306,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
308 get_geometry(view, &new_geo); 306 get_geometry(view, &new_geo);
309 307
310 if ((new_geo.width != view->width || new_geo.height != view->height) && 308 if ((new_geo.width != view->width || new_geo.height != view->height) &&
311 container_is_floating(view->swayc)) { 309 container_is_floating(view->container)) {
312 // A floating view has unexpectedly sent a new size 310 // A floating view has unexpectedly sent a new size
313 // eg. The Firefox "Save As" dialog when downloading a file 311 // eg. The Firefox "Save As" dialog when downloading a file
314 desktop_damage_view(view); 312 desktop_damage_view(view);
@@ -391,11 +389,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
391 view_map(view, xsurface->surface); 389 view_map(view, xsurface->surface);
392 390
393 if (xsurface->fullscreen) { 391 if (xsurface->fullscreen) {
394 container_set_fullscreen(view->swayc, true); 392 container_set_fullscreen(view->container, true);
395 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 393 arrange_workspace(view->container->workspace);
396 arrange_windows(ws);
397 } else { 394 } else {
398 arrange_windows(view->swayc->parent); 395 if (view->container->parent) {
396 arrange_container(view->container->parent);
397 } else {
398 arrange_workspace(view->container->workspace);
399 }
399 } 400 }
400 transaction_commit_dirty(); 401 transaction_commit_dirty();
401} 402}
@@ -411,13 +412,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
411 ev->width, ev->height); 412 ev->width, ev->height);
412 return; 413 return;
413 } 414 }
414 if (container_is_floating(view->swayc)) { 415 if (container_is_floating(view->container)) {
415 configure(view, view->swayc->current.view_x, 416 configure(view, view->container->current.view_x,
416 view->swayc->current.view_y, ev->width, ev->height); 417 view->container->current.view_y, ev->width, ev->height);
417 } else { 418 } else {
418 configure(view, view->swayc->current.view_x, 419 configure(view, view->container->current.view_x,
419 view->swayc->current.view_y, view->swayc->current.view_width, 420 view->container->current.view_y,
420 view->swayc->current.view_height); 421 view->container->current.view_width,
422 view->container->current.view_height);
421 } 423 }
422} 424}
423 425
@@ -429,10 +431,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
429 if (!xsurface->mapped) { 431 if (!xsurface->mapped) {
430 return; 432 return;
431 } 433 }
432 container_set_fullscreen(view->swayc, xsurface->fullscreen); 434 container_set_fullscreen(view->container, xsurface->fullscreen);
433 435
434 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 436 arrange_workspace(view->container->workspace);
435 arrange_windows(output);
436 transaction_commit_dirty(); 437 transaction_commit_dirty();
437} 438}
438 439
@@ -444,11 +445,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
444 if (!xsurface->mapped) { 445 if (!xsurface->mapped) {
445 return; 446 return;
446 } 447 }
447 if (!container_is_floating(view->swayc)) { 448 if (!container_is_floating(view->container)) {
448 return; 449 return;
449 } 450 }
450 struct sway_seat *seat = input_manager_current_seat(input_manager); 451 struct sway_seat *seat = input_manager_current_seat(input_manager);
451 seat_begin_move(seat, view->swayc, seat->last_button); 452 seat_begin_move(seat, view->container, seat->last_button);
452} 453}
453 454
454static void handle_request_resize(struct wl_listener *listener, void *data) { 455static void handle_request_resize(struct wl_listener *listener, void *data) {
@@ -459,12 +460,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
459 if (!xsurface->mapped) { 460 if (!xsurface->mapped) {
460 return; 461 return;
461 } 462 }
462 if (!container_is_floating(view->swayc)) { 463 if (!container_is_floating(view->container)) {
463 return; 464 return;
464 } 465 }
465 struct wlr_xwayland_resize_event *e = data; 466 struct wlr_xwayland_resize_event *e = data;
466 struct sway_seat *seat = input_manager_current_seat(input_manager); 467 struct sway_seat *seat = input_manager_current_seat(input_manager);
467 seat_begin_resize_floating(seat, view->swayc, seat->last_button, e->edges); 468 seat_begin_resize_floating(seat, view->container,
469 seat->last_button, e->edges);
468} 470}
469 471
470static void handle_request_activate(struct wl_listener *listener, void *data) { 472static void handle_request_activate(struct wl_listener *listener, void *data) {