summaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-06-23 16:24:11 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-06-23 16:24:11 +1000
commit38398e2d77d57dc06b67ec88a54091c897915602 (patch)
treec80935807865fd96ab7d037070287d4dfaba1863 /sway
parentPreserve buffers during transactions (diff)
downloadsway-38398e2d77d57dc06b67ec88a54091c897915602.tar.gz
sway-38398e2d77d57dc06b67ec88a54091c897915602.tar.zst
sway-38398e2d77d57dc06b67ec88a54091c897915602.zip
Implement atomic layout updates for tree operations
This implements atomic layout updates for when views map, reparent or unmap.
Diffstat (limited to 'sway')
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/desktop/output.c293
-rw-r--r--sway/desktop/transaction.c176
-rw-r--r--sway/desktop/xdg_shell.c35
-rw-r--r--sway/desktop/xdg_shell_v6.c30
-rw-r--r--sway/desktop/xwayland.c45
-rw-r--r--sway/main.c1
-rw-r--r--sway/server.c4
-rw-r--r--sway/tree/arrange.c25
-rw-r--r--sway/tree/container.c184
-rw-r--r--sway/tree/layout.c2
-rw-r--r--sway/tree/output.c2
-rw-r--r--sway/tree/view.c76
-rw-r--r--sway/tree/workspace.c3
14 files changed, 505 insertions, 373 deletions
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 7ea14953..c40f4d9f 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) {
16 } 16 }
17 struct sway_container *parent = container_split(con, layout); 17 struct sway_container *parent = container_split(con, layout);
18 container_create_notify(parent); 18 container_create_notify(parent);
19 arrange_and_commit(parent); 19 arrange_and_commit(parent->parent);
20 20
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22} 22}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a485cb10..9db95ef5 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -102,40 +102,8 @@ static bool get_surface_box(struct root_geometry *geo,
102 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); 102 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
103 103
104 struct wlr_box output_box = { 104 struct wlr_box output_box = {
105 .width = output->swayc->width, 105 .width = output->swayc->current.swayc_width,
106 .height = output->swayc->height, 106 .height = output->swayc->current.swayc_height,
107 };
108
109 struct wlr_box intersection;
110 return wlr_box_intersection(&output_box, &rotated_box, &intersection);
111}
112
113static bool get_view_box(struct root_geometry *geo,
114 struct sway_output *output, struct sway_view *view, int sx, int sy,
115 struct wlr_box *surface_box) {
116 int sw = view->saved_surface_width;
117 int sh = view->saved_surface_height;
118
119 double _sx = sx, _sy = sy;
120 rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
121 geo->rotation);
122
123 struct wlr_box box = {
124 .x = geo->x + _sx,
125 .y = geo->y + _sy,
126 .width = sw,
127 .height = sh,
128 };
129 if (surface_box != NULL) {
130 memcpy(surface_box, &box, sizeof(struct wlr_box));
131 }
132
133 struct wlr_box rotated_box;
134 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
135
136 struct wlr_box output_box = {
137 .width = output->swayc->width,
138 .height = output->swayc->height,
139 }; 107 };
140 108
141 struct wlr_box intersection; 109 struct wlr_box intersection;
@@ -158,8 +126,8 @@ static void output_view_for_each_surface(struct sway_view *view,
158 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 126 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
159 void *user_data) { 127 void *user_data) {
160 struct render_data *data = user_data; 128 struct render_data *data = user_data;
161 geo->x = view->swayc->current.view_x - data->output->swayc->x; 129 geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x;
162 geo->y = view->swayc->current.view_y - data->output->swayc->y; 130 geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y;
163 geo->width = view->swayc->current.view_width; 131 geo->width = view->swayc->current.view_width;
164 geo->height = view->swayc->current.view_height; 132 geo->height = view->swayc->current.view_height;
165 geo->rotation = 0; // TODO 133 geo->rotation = 0; // TODO
@@ -187,8 +155,8 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged,
187 wl_list_for_each(unmanaged_surface, unmanaged, link) { 155 wl_list_for_each(unmanaged_surface, unmanaged, link) {
188 struct wlr_xwayland_surface *xsurface = 156 struct wlr_xwayland_surface *xsurface =
189 unmanaged_surface->wlr_xwayland_surface; 157 unmanaged_surface->wlr_xwayland_surface;
190 double ox = unmanaged_surface->lx - output->swayc->x; 158 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x;
191 double oy = unmanaged_surface->ly - output->swayc->y; 159 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y;
192 160
193 surface_for_each_surface(xsurface->surface, ox, oy, geo, 161 surface_for_each_surface(xsurface->surface, ox, oy, geo,
194 iterator, user_data); 162 iterator, user_data);
@@ -274,26 +242,14 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
274 pixman_region32_t *output_damage = data->damage; 242 pixman_region32_t *output_damage = data->damage;
275 float alpha = data->alpha; 243 float alpha = data->alpha;
276 244
277 struct wlr_texture *texture = NULL; 245 struct wlr_texture *texture = wlr_surface_get_texture(surface);
278 struct wlr_box box; 246 if (!texture) {
279 bool intersects; 247 return;
280
281 // If this is the main surface of a view, render the saved_buffer instead
282 // if it exists. It exists when we are mid-transaction.
283 if (data->view && data->view->saved_buffer &&
284 data->view->surface == surface) {
285 texture = data->view->saved_buffer->texture;
286 intersects = get_view_box(&data->root_geo, data->output, data->view,
287 sx, sy, &box);
288 } else {
289 texture = wlr_surface_get_texture(surface);
290 if (texture == NULL) {
291 return;
292 }
293 intersects = get_surface_box(&data->root_geo, data->output, surface,
294 sx, sy, &box);
295 } 248 }
296 249
250 struct wlr_box box;
251 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
252 sx, sy, &box);
297 if (!intersects) { 253 if (!intersects) {
298 return; 254 return;
299 } 255 }
@@ -394,58 +350,98 @@ static void render_view_surfaces(struct sway_view *view,
394 view, &data.root_geo, render_surface_iterator, &data); 350 view, &data.root_geo, render_surface_iterator, &data);
395} 351}
396 352
353static void render_saved_view(struct sway_view *view,
354 struct sway_output *output, pixman_region32_t *damage, float alpha) {
355 struct wlr_output *wlr_output = output->wlr_output;
356
357 struct wlr_texture *texture = transaction_get_texture(view);
358 if (!texture) {
359 return;
360 }
361 struct wlr_box box = {
362 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
363 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
364 .width = view->swayc->current.view_width,
365 .height = view->swayc->current.view_height,
366 };
367
368 struct wlr_box output_box = {
369 .width = output->swayc->current.swayc_width,
370 .height = output->swayc->current.swayc_height,
371 };
372
373 struct wlr_box intersection;
374 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
375 if (!intersects) {
376 return;
377 }
378
379 scale_box(&box, wlr_output->scale);
380
381 float matrix[9];
382 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
383 wlr_output->transform_matrix);
384
385 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
386}
387
397/** 388/**
398 * Render a view's surface and left/bottom/right borders. 389 * Render a view's surface and left/bottom/right borders.
399 */ 390 */
400static void render_view(struct sway_output *output, pixman_region32_t *damage, 391static void render_view(struct sway_output *output, pixman_region32_t *damage,
401 struct sway_container *con, struct border_colors *colors) { 392 struct sway_container *con, struct border_colors *colors) {
402 struct sway_view *view = con->sway_view; 393 struct sway_view *view = con->sway_view;
403 render_view_surfaces(view, output, damage, view->swayc->alpha); 394 if (view->swayc->instructions->length) {
395 render_saved_view(view, output, damage, view->swayc->alpha);
396 } else {
397 render_view_surfaces(view, output, damage, view->swayc->alpha);
398 }
404 399
405 struct wlr_box box; 400 struct wlr_box box;
406 float output_scale = output->wlr_output->scale; 401 float output_scale = output->wlr_output->scale;
407 float color[4]; 402 float color[4];
403 struct sway_container_state *state = &con->current;
408 404
409 if (con->current.border != B_NONE) { 405 if (state->border != B_NONE) {
410 if (con->current.border_left) { 406 if (state->border_left) {
411 memcpy(&color, colors->child_border, sizeof(float) * 4); 407 memcpy(&color, colors->child_border, sizeof(float) * 4);
412 premultiply_alpha(color, con->alpha); 408 premultiply_alpha(color, con->alpha);
413 box.x = con->current.swayc_x; 409 box.x = state->swayc_x;
414 box.y = con->current.view_y; 410 box.y = state->view_y;
415 box.width = con->current.border_thickness; 411 box.width = state->border_thickness;
416 box.height = con->current.view_height; 412 box.height = state->view_height;
417 scale_box(&box, output_scale); 413 scale_box(&box, output_scale);
418 render_rect(output->wlr_output, damage, &box, color); 414 render_rect(output->wlr_output, damage, &box, color);
419 } 415 }
420 416
421 if (con->current.border_right) { 417 if (state->border_right) {
422 if (con->parent->children->length == 1 418 if (state->parent->current.children->length == 1
423 && con->parent->current.layout == L_HORIZ) { 419 && state->parent->current.layout == L_HORIZ) {
424 memcpy(&color, colors->indicator, sizeof(float) * 4); 420 memcpy(&color, colors->indicator, sizeof(float) * 4);
425 } else { 421 } else {
426 memcpy(&color, colors->child_border, sizeof(float) * 4); 422 memcpy(&color, colors->child_border, sizeof(float) * 4);
427 } 423 }
428 premultiply_alpha(color, con->alpha); 424 premultiply_alpha(color, con->alpha);
429 box.x = con->current.view_x + con->current.view_width; 425 box.x = state->view_x + state->view_width;
430 box.y = con->current.view_y; 426 box.y = state->view_y;
431 box.width = con->current.border_thickness; 427 box.width = state->border_thickness;
432 box.height = con->current.view_height; 428 box.height = state->view_height;
433 scale_box(&box, output_scale); 429 scale_box(&box, output_scale);
434 render_rect(output->wlr_output, damage, &box, color); 430 render_rect(output->wlr_output, damage, &box, color);
435 } 431 }
436 432
437 if (con->current.border_bottom) { 433 if (state->border_bottom) {
438 if (con->parent->children->length == 1 434 if (state->parent->current.children->length == 1
439 && con->parent->current.layout == L_VERT) { 435 && con->current.parent->current.layout == L_VERT) {
440 memcpy(&color, colors->indicator, sizeof(float) * 4); 436 memcpy(&color, colors->indicator, sizeof(float) * 4);
441 } else { 437 } else {
442 memcpy(&color, colors->child_border, sizeof(float) * 4); 438 memcpy(&color, colors->child_border, sizeof(float) * 4);
443 } 439 }
444 premultiply_alpha(color, con->alpha); 440 premultiply_alpha(color, con->alpha);
445 box.x = con->current.swayc_x; 441 box.x = state->swayc_x;
446 box.y = con->current.view_y + con->current.view_height; 442 box.y = state->view_y + state->view_height;
447 box.width = con->current.swayc_width; 443 box.width = state->swayc_width;
448 box.height = con->current.border_thickness; 444 box.height = state->border_thickness;
449 scale_box(&box, output_scale); 445 scale_box(&box, output_scale);
450 render_rect(output->wlr_output, damage, &box, color); 446 render_rect(output->wlr_output, damage, &box, color);
451 } 447 }
@@ -469,10 +465,13 @@ static void render_titlebar(struct sway_output *output,
469 struct wlr_texture *marks_texture) { 465 struct wlr_texture *marks_texture) {
470 struct wlr_box box; 466 struct wlr_box box;
471 float color[4]; 467 float color[4];
468 struct sway_container_state *state = &con->current;
472 float output_scale = output->wlr_output->scale; 469 float output_scale = output->wlr_output->scale;
473 enum sway_container_layout layout = con->parent->current.layout; 470 enum sway_container_layout layout = state->parent->current.layout;
474 bool is_last_child = 471 list_t *children = state->parent->current.children;
475 con->parent->children->items[con->parent->children->length - 1] == con; 472 bool is_last_child = children->items[children->length - 1] == con;
473 double output_x = output->swayc->current.swayc_x;
474 double output_y = output->swayc->current.swayc_y;
476 475
477 // Single pixel bar above title 476 // Single pixel bar above title
478 memcpy(&color, colors->border, sizeof(float) * 4); 477 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -490,10 +489,8 @@ static void render_titlebar(struct sway_output *output,
490 if (layout == L_HORIZ || layout == L_VERT || 489 if (layout == L_HORIZ || layout == L_VERT ||
491 (layout == L_STACKED && is_last_child)) { 490 (layout == L_STACKED && is_last_child)) {
492 if (con->type == C_VIEW) { 491 if (con->type == C_VIEW) {
493 left_offset = 492 left_offset = state->border_left * state->border_thickness;
494 con->current.border_left * con->current.border_thickness; 493 right_offset = state->border_right * state->border_thickness;
495 right_offset =
496 con->current.border_right * con->current.border_thickness;
497 connects_sides = true; 494 connects_sides = true;
498 } 495 }
499 } 496 }
@@ -527,10 +524,9 @@ static void render_titlebar(struct sway_output *output,
527 struct wlr_box texture_box; 524 struct wlr_box texture_box;
528 wlr_texture_get_size(marks_texture, 525 wlr_texture_get_size(marks_texture,
529 &texture_box.width, &texture_box.height); 526 &texture_box.width, &texture_box.height);
530 texture_box.x = (x - output->swayc->x + width - TITLEBAR_H_PADDING) 527 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
531 * output_scale - texture_box.width; 528 * output_scale - texture_box.width;
532 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) 529 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
533 * output_scale;
534 530
535 float matrix[9]; 531 float matrix[9];
536 wlr_matrix_project_box(matrix, &texture_box, 532 wlr_matrix_project_box(matrix, &texture_box,
@@ -551,10 +547,8 @@ static void render_titlebar(struct sway_output *output,
551 struct wlr_box texture_box; 547 struct wlr_box texture_box;
552 wlr_texture_get_size(title_texture, 548 wlr_texture_get_size(title_texture,
553 &texture_box.width, &texture_box.height); 549 &texture_box.width, &texture_box.height);
554 texture_box.x = (x - output->swayc->x + TITLEBAR_H_PADDING) 550 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
555 * output_scale; 551 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
556 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING)
557 * output_scale;
558 552
559 float matrix[9]; 553 float matrix[9];
560 wlr_matrix_project_box(matrix, &texture_box, 554 wlr_matrix_project_box(matrix, &texture_box,
@@ -614,16 +608,15 @@ static void render_titlebar(struct sway_output *output,
614 // Left pixel in line with bottom bar 608 // Left pixel in line with bottom bar
615 box.x = x; 609 box.x = x;
616 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 610 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
617 box.width = con->current.border_thickness * con->current.border_left; 611 box.width = state->border_thickness * state->border_left;
618 box.height = TITLEBAR_BORDER_THICKNESS; 612 box.height = TITLEBAR_BORDER_THICKNESS;
619 scale_box(&box, output_scale); 613 scale_box(&box, output_scale);
620 render_rect(output->wlr_output, output_damage, &box, color); 614 render_rect(output->wlr_output, output_damage, &box, color);
621 615
622 // Right pixel in line with bottom bar 616 // Right pixel in line with bottom bar
623 box.x = x + width - 617 box.x = x + width - state->border_thickness * state->border_right;
624 con->current.border_thickness * con->current.border_right;
625 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 618 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
626 box.width = con->current.border_thickness * con->current.border_right; 619 box.width = state->border_thickness * state->border_right;
627 box.height = TITLEBAR_BORDER_THICKNESS; 620 box.height = TITLEBAR_BORDER_THICKNESS;
628 scale_box(&box, output_scale); 621 scale_box(&box, output_scale);
629 render_rect(output->wlr_output, output_damage, &box, color); 622 render_rect(output->wlr_output, output_damage, &box, color);
@@ -636,7 +629,8 @@ static void render_titlebar(struct sway_output *output,
636static void render_top_border(struct sway_output *output, 629static void render_top_border(struct sway_output *output,
637 pixman_region32_t *output_damage, struct sway_container *con, 630 pixman_region32_t *output_damage, struct sway_container *con,
638 struct border_colors *colors) { 631 struct border_colors *colors) {
639 if (!con->current.border_top) { 632 struct sway_container_state *state = &con->current;
633 if (!state->border_top) {
640 return; 634 return;
641 } 635 }
642 struct wlr_box box; 636 struct wlr_box box;
@@ -646,10 +640,10 @@ static void render_top_border(struct sway_output *output,
646 // Child border - top edge 640 // Child border - top edge
647 memcpy(&color, colors->child_border, sizeof(float) * 4); 641 memcpy(&color, colors->child_border, sizeof(float) * 4);
648 premultiply_alpha(color, con->alpha); 642 premultiply_alpha(color, con->alpha);
649 box.x = con->current.swayc_x; 643 box.x = state->swayc_x;
650 box.y = con->current.swayc_y; 644 box.y = state->swayc_y;
651 box.width = con->current.swayc_width; 645 box.width = state->swayc_width;
652 box.height = con->current.border_thickness; 646 box.height = state->border_thickness;
653 scale_box(&box, output_scale); 647 scale_box(&box, output_scale);
654 render_rect(output->wlr_output, output_damage, &box, color); 648 render_rect(output->wlr_output, output_damage, &box, color);
655} 649}
@@ -669,31 +663,34 @@ static void render_container_simple(struct sway_output *output,
669 struct sway_seat *seat = input_manager_current_seat(input_manager); 663 struct sway_seat *seat = input_manager_current_seat(input_manager);
670 struct sway_container *focus = seat_get_focus(seat); 664 struct sway_container *focus = seat_get_focus(seat);
671 665
672 for (int i = 0; i < con->children->length; ++i) { 666 for (int i = 0; i < con->current.children->length; ++i) {
673 struct sway_container *child = con->children->items[i]; 667 struct sway_container *child = con->current.children->items[i];
674 668
675 if (child->type == C_VIEW) { 669 if (child->type == C_VIEW) {
670 struct sway_view *view = child->sway_view;
676 struct border_colors *colors; 671 struct border_colors *colors;
677 struct wlr_texture *title_texture; 672 struct wlr_texture *title_texture;
678 struct wlr_texture *marks_texture; 673 struct wlr_texture *marks_texture;
674 struct sway_container_state *state = &child->current;
675
679 if (focus == child || parent_focused) { 676 if (focus == child || parent_focused) {
680 colors = &config->border_colors.focused; 677 colors = &config->border_colors.focused;
681 title_texture = child->title_focused; 678 title_texture = child->title_focused;
682 marks_texture = child->sway_view->marks_focused; 679 marks_texture = view->marks_focused;
683 } else if (seat_get_focus_inactive(seat, con) == child) { 680 } else if (seat_get_focus_inactive(seat, con) == child) {
684 colors = &config->border_colors.focused_inactive; 681 colors = &config->border_colors.focused_inactive;
685 title_texture = child->title_focused_inactive; 682 title_texture = child->title_focused_inactive;
686 marks_texture = child->sway_view->marks_focused_inactive; 683 marks_texture = view->marks_focused_inactive;
687 } else { 684 } else {
688 colors = &config->border_colors.unfocused; 685 colors = &config->border_colors.unfocused;
689 title_texture = child->title_unfocused; 686 title_texture = child->title_unfocused;
690 marks_texture = child->sway_view->marks_unfocused; 687 marks_texture = view->marks_unfocused;
691 } 688 }
692 689
693 if (child->current.border == B_NORMAL) { 690 if (state->border == B_NORMAL) {
694 render_titlebar(output, damage, child, child->current.swayc_x, 691 render_titlebar(output, damage, child, state->swayc_x,
695 child->current.swayc_y, child->current.swayc_width, 692 state->swayc_y, state->swayc_width, colors,
696 colors, title_texture, marks_texture); 693 title_texture, marks_texture);
697 } else { 694 } else {
698 render_top_border(output, damage, child, colors); 695 render_top_border(output, damage, child, colors);
699 } 696 }
@@ -711,22 +708,23 @@ static void render_container_simple(struct sway_output *output,
711static void render_container_tabbed(struct sway_output *output, 708static void render_container_tabbed(struct sway_output *output,
712 pixman_region32_t *damage, struct sway_container *con, 709 pixman_region32_t *damage, struct sway_container *con,
713 bool parent_focused) { 710 bool parent_focused) {
714 if (!con->children->length) { 711 if (!con->current.children->length) {
715 return; 712 return;
716 } 713 }
717 struct sway_seat *seat = input_manager_current_seat(input_manager); 714 struct sway_seat *seat = input_manager_current_seat(input_manager);
718 struct sway_container *focus = seat_get_focus(seat); 715 struct sway_container *focus = seat_get_focus(seat);
719 struct sway_container *current = seat_get_active_child(seat, con); 716 struct sway_container *current = seat_get_active_child(seat, con);
720 struct border_colors *current_colors = NULL; 717 struct border_colors *current_colors = NULL;
718 struct sway_container_state *pstate = &con->current;
721 719
722 // Render tabs 720 // Render tabs
723 for (int i = 0; i < con->children->length; ++i) { 721 for (int i = 0; i < con->current.children->length; ++i) {
724 struct sway_container *child = con->children->items[i]; 722 struct sway_container *child = con->current.children->items[i];
723 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
724 struct sway_container_state *cstate = &child->current;
725 struct border_colors *colors; 725 struct border_colors *colors;
726 struct wlr_texture *title_texture; 726 struct wlr_texture *title_texture;
727 struct wlr_texture *marks_texture; 727 struct wlr_texture *marks_texture;
728 struct sway_view *view =
729 child->type == C_VIEW ? child->sway_view : NULL;
730 728
731 if (focus == child || parent_focused) { 729 if (focus == child || parent_focused) {
732 colors = &config->border_colors.focused; 730 colors = &config->border_colors.focused;
@@ -735,22 +733,22 @@ static void render_container_tabbed(struct sway_output *output,
735 } else if (child == current) { 733 } else if (child == current) {
736 colors = &config->border_colors.focused_inactive; 734 colors = &config->border_colors.focused_inactive;
737 title_texture = child->title_focused_inactive; 735 title_texture = child->title_focused_inactive;
738 marks_texture = view ? view->marks_focused : NULL; 736 marks_texture = view ? view->marks_focused_inactive : NULL;
739 } else { 737 } else {
740 colors = &config->border_colors.unfocused; 738 colors = &config->border_colors.unfocused;
741 title_texture = child->title_unfocused; 739 title_texture = child->title_unfocused;
742 marks_texture = view ? view->marks_unfocused : NULL; 740 marks_texture = view ? view->marks_unfocused : NULL;
743 } 741 }
744 742
745 int tab_width = con->current.swayc_width / con->children->length; 743 int tab_width = pstate->swayc_width / pstate->children->length;
746 int x = con->current.swayc_x + tab_width * i; 744 int x = pstate->swayc_x + tab_width * i;
747 // Make last tab use the remaining width of the parent 745 // Make last tab use the remaining width of the parent
748 if (i == con->children->length - 1) { 746 if (i == pstate->children->length - 1) {
749 tab_width = con->current.swayc_width - tab_width * i; 747 tab_width = pstate->swayc_width - tab_width * i;
750 } 748 }
751 749
752 render_titlebar(output, damage, child, x, child->current.swayc_y, 750 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
753 tab_width, colors, title_texture, marks_texture); 751 colors, title_texture, marks_texture);
754 752
755 if (child == current) { 753 if (child == current) {
756 current_colors = colors; 754 current_colors = colors;
@@ -772,22 +770,23 @@ static void render_container_tabbed(struct sway_output *output,
772static void render_container_stacked(struct sway_output *output, 770static void render_container_stacked(struct sway_output *output,
773 pixman_region32_t *damage, struct sway_container *con, 771 pixman_region32_t *damage, struct sway_container *con,
774 bool parent_focused) { 772 bool parent_focused) {
775 if (!con->children->length) { 773 if (!con->current.children->length) {
776 return; 774 return;
777 } 775 }
778 struct sway_seat *seat = input_manager_current_seat(input_manager); 776 struct sway_seat *seat = input_manager_current_seat(input_manager);
779 struct sway_container *focus = seat_get_focus(seat); 777 struct sway_container *focus = seat_get_focus(seat);
780 struct sway_container *current = seat_get_active_child(seat, con); 778 struct sway_container *current = seat_get_active_child(seat, con);
781 struct border_colors *current_colors = NULL; 779 struct border_colors *current_colors = NULL;
780 struct sway_container_state *pstate = &con->current;
782 781
783 // Render titles 782 // Render titles
784 for (int i = 0; i < con->children->length; ++i) { 783 for (int i = 0; i < con->current.children->length; ++i) {
785 struct sway_container *child = con->children->items[i]; 784 struct sway_container *child = con->current.children->items[i];
785 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
786 struct sway_container_state *cstate = &child->current;
786 struct border_colors *colors; 787 struct border_colors *colors;
787 struct wlr_texture *title_texture; 788 struct wlr_texture *title_texture;
788 struct wlr_texture *marks_texture; 789 struct wlr_texture *marks_texture;
789 struct sway_view *view =
790 child->type == C_VIEW ? child->sway_view : NULL;
791 790
792 if (focus == child || parent_focused) { 791 if (focus == child || parent_focused) {
793 colors = &config->border_colors.focused; 792 colors = &config->border_colors.focused;
@@ -803,10 +802,9 @@ static void render_container_stacked(struct sway_output *output,
803 marks_texture = view ? view->marks_unfocused : NULL; 802 marks_texture = view ? view->marks_unfocused : NULL;
804 } 803 }
805 804
806 int y = con->current.swayc_y + container_titlebar_height() * i; 805 int y = pstate->swayc_y + container_titlebar_height() * i;
807 render_titlebar(output, damage, child, child->current.swayc_x, y, 806 render_titlebar(output, damage, child, cstate->swayc_x, y,
808 child->current.swayc_width, colors, 807 cstate->swayc_width, colors, title_texture, marks_texture);
809 title_texture, marks_texture);
810 808
811 if (child == current) { 809 if (child == current) {
812 current_colors = colors; 810 current_colors = colors;
@@ -877,17 +875,18 @@ static void render_floating_container(struct sway_output *soutput,
877 875
878static void render_floating(struct sway_output *soutput, 876static void render_floating(struct sway_output *soutput,
879 pixman_region32_t *damage) { 877 pixman_region32_t *damage) {
880 for (int i = 0; i < root_container.children->length; ++i) { 878 for (int i = 0; i < root_container.current.children->length; ++i) {
881 struct sway_container *output = root_container.children->items[i]; 879 struct sway_container *output =
882 for (int j = 0; j < output->children->length; ++j) { 880 root_container.current.children->items[i];
883 struct sway_container *workspace = output->children->items[j]; 881 for (int j = 0; j < output->current.children->length; ++j) {
884 struct sway_workspace *ws = workspace->sway_workspace; 882 struct sway_container *ws = output->current.children->items[j];
885 if (!workspace_is_visible(workspace)) { 883 if (!workspace_is_visible(ws)) {
886 continue; 884 continue;
887 } 885 }
888 for (int k = 0; k < ws->floating->children->length; ++k) { 886 list_t *floating =
889 struct sway_container *floater = 887 ws->current.ws_floating->current.children;
890 ws->floating->children->items[k]; 888 for (int k = 0; k < floating->length; ++k) {
889 struct sway_container *floater = floating->items[k];
891 render_floating_container(soutput, damage, floater); 890 render_floating_container(soutput, damage, floater);
892 } 891 }
893 } 892 }
@@ -901,7 +900,7 @@ static struct sway_container *output_get_active_workspace(
901 seat_get_focus_inactive(seat, output->swayc); 900 seat_get_focus_inactive(seat, output->swayc);
902 if (!focus) { 901 if (!focus) {
903 // We've never been to this output before 902 // We've never been to this output before
904 focus = output->swayc->children->items[0]; 903 focus = output->swayc->current.children->items[0];
905 } 904 }
906 struct sway_container *workspace = focus; 905 struct sway_container *workspace = focus;
907 if (workspace->type != C_WORKSPACE) { 906 if (workspace->type != C_WORKSPACE) {
@@ -942,8 +941,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
942 } 941 }
943 942
944 struct sway_container *workspace = output_get_active_workspace(output); 943 struct sway_container *workspace = output_get_active_workspace(output);
944 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
945 945
946 if (workspace->sway_workspace->fullscreen) { 946 if (fullscreen_view) {
947 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; 947 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
948 948
949 int nrects; 949 int nrects;
@@ -954,10 +954,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
954 } 954 }
955 955
956 // TODO: handle views smaller than the output 956 // TODO: handle views smaller than the output
957 render_view_surfaces( 957 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
958 workspace->sway_workspace->fullscreen, output, damage, 1.0f);
959 958
960 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 959 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
961 render_unmanaged(output, damage, 960 render_unmanaged(output, damage,
962 &root_container.sway_root->xwayland_unmanaged); 961 &root_container.sway_root->xwayland_unmanaged);
963 } 962 }
@@ -1073,11 +1072,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
1073 }; 1072 };
1074 1073
1075 struct sway_container *workspace = output_get_active_workspace(output); 1074 struct sway_container *workspace = output_get_active_workspace(output);
1076 if (workspace->sway_workspace->fullscreen) { 1075 if (workspace->current.ws_fullscreen) {
1077 send_frame_done_container_iterator( 1076 send_frame_done_container_iterator(
1078 workspace->sway_workspace->fullscreen->swayc, &data); 1077 workspace->current.ws_fullscreen->swayc, &data);
1079 1078
1080 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 1079 if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) {
1081 send_frame_done_unmanaged(&data, 1080 send_frame_done_unmanaged(&data,
1082 &root_container.sway_root->xwayland_unmanaged); 1081 &root_container.sway_root->xwayland_unmanaged);
1083 } 1082 }
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 77377a18..6e09537a 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -9,6 +9,7 @@
9#include "sway/output.h" 9#include "sway/output.h"
10#include "sway/tree/container.h" 10#include "sway/tree/container.h"
11#include "sway/tree/view.h" 11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
12#include "list.h" 13#include "list.h"
13#include "log.h" 14#include "log.h"
14 15
@@ -18,6 +19,13 @@
18 */ 19 */
19#define TIMEOUT_MS 200 20#define TIMEOUT_MS 200
20 21
22/**
23 * If enabled, sway will always wait for the transaction timeout before
24 * applying it, rather than applying it when the views are ready. This allows us
25 * to observe the rendered state while a transaction is in progress.
26 */
27#define TRANSACTION_DEBUG false
28
21struct sway_transaction { 29struct sway_transaction {
22 struct wl_event_source *timer; 30 struct wl_event_source *timer;
23 list_t *instructions; // struct sway_transaction_instruction * 31 list_t *instructions; // struct sway_transaction_instruction *
@@ -29,7 +37,9 @@ struct sway_transaction_instruction {
29 struct sway_transaction *transaction; 37 struct sway_transaction *transaction;
30 struct sway_container *container; 38 struct sway_container *container;
31 struct sway_container_state state; 39 struct sway_container_state state;
40 struct wlr_buffer *saved_buffer;
32 uint32_t serial; 41 uint32_t serial;
42 bool ready;
33}; 43};
34 44
35struct sway_transaction *transaction_create() { 45struct sway_transaction *transaction_create() {
@@ -40,44 +50,55 @@ struct sway_transaction *transaction_create() {
40 return transaction; 50 return transaction;
41} 51}
42 52
53static void remove_saved_view_buffer(
54 struct sway_transaction_instruction *instruction) {
55 if (instruction->saved_buffer) {
56 wlr_buffer_unref(instruction->saved_buffer);
57 instruction->saved_buffer = NULL;
58 }
59}
60
61static void save_view_buffer(struct sway_view *view,
62 struct sway_transaction_instruction *instruction) {
63 if (!sway_assert(instruction->saved_buffer == NULL,
64 "Didn't expect instruction to have a saved buffer already")) {
65 remove_saved_view_buffer(instruction);
66 }
67 if (view->surface && wlr_surface_has_buffer(view->surface)) {
68 wlr_buffer_ref(view->surface->buffer);
69 instruction->saved_buffer = view->surface->buffer;
70 }
71}
72
43static void transaction_destroy(struct sway_transaction *transaction) { 73static void transaction_destroy(struct sway_transaction *transaction) {
44 int i;
45 // Free instructions 74 // Free instructions
46 for (i = 0; i < transaction->instructions->length; ++i) { 75 for (int i = 0; i < transaction->instructions->length; ++i) {
47 struct sway_transaction_instruction *instruction = 76 struct sway_transaction_instruction *instruction =
48 transaction->instructions->items[i]; 77 transaction->instructions->items[i];
49 if (instruction->container->type == C_VIEW) { 78 struct sway_container *con = instruction->container;
50 struct sway_view *view = instruction->container->sway_view; 79 for (int j = 0; j < con->instructions->length; ++j) {
51 for (int j = 0; j < view->instructions->length; ++j) { 80 if (con->instructions->items[j] == instruction) {
52 if (view->instructions->items[j] == instruction) { 81 list_del(con->instructions, j);
53 list_del(view->instructions, j); 82 break;
54 break;
55 }
56 } 83 }
57 } 84 }
85 if (con->destroying && !con->instructions->length) {
86 container_free(con);
87 }
88 remove_saved_view_buffer(instruction);
58 free(instruction); 89 free(instruction);
59 } 90 }
60 list_free(transaction->instructions); 91 list_free(transaction->instructions);
61 92
62 // Free damage 93 // Free damage
63 for (i = 0; i < transaction->damage->length; ++i) { 94 list_foreach(transaction->damage, free);
64 struct wlr_box *box = transaction->damage->items[i];
65 free(box);
66 }
67 list_free(transaction->damage); 95 list_free(transaction->damage);
68 96
69 free(transaction); 97 free(transaction);
70} 98}
71 99
72void transaction_add_container(struct sway_transaction *transaction, 100static void copy_pending_state(struct sway_container *container,
73 struct sway_container *container) { 101 struct sway_container_state *state) {
74 struct sway_transaction_instruction *instruction =
75 calloc(1, sizeof(struct sway_transaction_instruction));
76 instruction->transaction = transaction;
77 instruction->container = container;
78
79 // Copy the container's main (pending) properties into the instruction state
80 struct sway_container_state *state = &instruction->state;
81 state->layout = container->layout; 102 state->layout = container->layout;
82 state->swayc_x = container->x; 103 state->swayc_x = container->x;
83 state->swayc_y = container->y; 104 state->swayc_y = container->y;
@@ -87,6 +108,7 @@ void transaction_add_container(struct sway_transaction *transaction,
87 state->current_gaps = container->current_gaps; 108 state->current_gaps = container->current_gaps;
88 state->gaps_inner = container->gaps_inner; 109 state->gaps_inner = container->gaps_inner;
89 state->gaps_outer = container->gaps_outer; 110 state->gaps_outer = container->gaps_outer;
111 state->parent = container->parent;
90 112
91 if (container->type == C_VIEW) { 113 if (container->type == C_VIEW) {
92 struct sway_view *view = container->sway_view; 114 struct sway_view *view = container->sway_view;
@@ -101,8 +123,44 @@ void transaction_add_container(struct sway_transaction *transaction,
101 state->border_left = view->border_left; 123 state->border_left = view->border_left;
102 state->border_right = view->border_right; 124 state->border_right = view->border_right;
103 state->border_bottom = view->border_bottom; 125 state->border_bottom = view->border_bottom;
126 } else if (container->type == C_WORKSPACE) {
127 state->ws_fullscreen = container->sway_workspace->fullscreen;
128 state->ws_floating = container->sway_workspace->floating;
129 state->children = create_list();
130 list_cat(state->children, container->children);
131 } else {
132 state->children = create_list();
133 list_cat(state->children, container->children);
134 }
135}
136
137static bool transaction_has_container(struct sway_transaction *transaction,
138 struct sway_container *container) {
139 for (int i = 0; i < transaction->instructions->length; ++i) {
140 struct sway_transaction_instruction *instruction =
141 transaction->instructions->items[i];
142 if (instruction->container == container) {
143 return true;
144 }
145 }
146 return false;
147}
148
149void transaction_add_container(struct sway_transaction *transaction,
150 struct sway_container *container) {
151 if (transaction_has_container(transaction, container)) {
152 return;
104 } 153 }
154 struct sway_transaction_instruction *instruction =
155 calloc(1, sizeof(struct sway_transaction_instruction));
156 instruction->transaction = transaction;
157 instruction->container = container;
158
159 copy_pending_state(container, &instruction->state);
105 160
161 if (container->type == C_VIEW) {
162 save_view_buffer(container->sway_view, instruction);
163 }
106 list_add(transaction->instructions, instruction); 164 list_add(transaction->instructions, instruction);
107} 165}
108 166
@@ -113,47 +171,29 @@ void transaction_add_damage(struct sway_transaction *transaction,
113 list_add(transaction->damage, box); 171 list_add(transaction->damage, box);
114} 172}
115 173
116static void save_view_buffer(struct sway_view *view) {
117 if (view->saved_buffer) {
118 wlr_buffer_unref(view->saved_buffer);
119 }
120 wlr_buffer_ref(view->surface->buffer);
121 view->saved_buffer = view->surface->buffer;
122 view->saved_surface_width = view->surface->current->width;
123 view->saved_surface_height = view->surface->current->height;
124}
125
126static void remove_saved_view_buffer(struct sway_view *view) {
127 if (view->saved_buffer) {
128 wlr_buffer_unref(view->saved_buffer);
129 view->saved_buffer = NULL;
130 view->saved_surface_width = 0;
131 view->saved_surface_height = 0;
132 }
133}
134
135/** 174/**
136 * Apply a transaction to the "current" state of the tree. 175 * Apply a transaction to the "current" state of the tree.
137 *
138 * This is mostly copying stuff from the pending state into the main swayc
139 * properties, but also includes reparenting and deleting containers.
140 */ 176 */
141static void transaction_apply(struct sway_transaction *transaction) { 177static void transaction_apply(struct sway_transaction *transaction) {
142 int i; 178 int i;
179 // Apply the instruction state to the container's current state
143 for (i = 0; i < transaction->instructions->length; ++i) { 180 for (i = 0; i < transaction->instructions->length; ++i) {
144 struct sway_transaction_instruction *instruction = 181 struct sway_transaction_instruction *instruction =
145 transaction->instructions->items[i]; 182 transaction->instructions->items[i];
146 struct sway_container *container = instruction->container; 183 struct sway_container *container = instruction->container;
147 184
148 memcpy(&instruction->container->current, &instruction->state, 185 // There are separate children lists for each instruction state, the
149 sizeof(struct sway_container_state)); 186 // container's current state and the container's pending state
187 // (ie. con->children). The list itself needs to be freed here.
188 // Any child containers which are being deleted will be cleaned up in
189 // transaction_destroy().
190 list_free(container->current.children);
150 191
151 if (container->type == C_VIEW) { 192 memcpy(&container->current, &instruction->state,
152 remove_saved_view_buffer(container->sway_view); 193 sizeof(struct sway_container_state));
153 }
154 } 194 }
155 195
156 // Damage 196 // Apply damage
157 for (i = 0; i < transaction->damage->length; ++i) { 197 for (i = 0; i < transaction->damage->length; ++i) {
158 struct wlr_box *box = transaction->damage->items[i]; 198 struct wlr_box *box = transaction->damage->items[i];
159 for (int j = 0; j < root_container.children->length; ++j) { 199 for (int j = 0; j < root_container.children->length; ++j) {
@@ -161,8 +201,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
161 output_damage_box(output->sway_output, box); 201 output_damage_box(output->sway_output, box);
162 } 202 }
163 } 203 }
164
165 update_debug_tree();
166} 204}
167 205
168static int handle_timeout(void *data) { 206static int handle_timeout(void *data) {
@@ -182,7 +220,7 @@ void transaction_commit(struct sway_transaction *transaction) {
182 struct sway_transaction_instruction *instruction = 220 struct sway_transaction_instruction *instruction =
183 transaction->instructions->items[i]; 221 transaction->instructions->items[i];
184 struct sway_container *con = instruction->container; 222 struct sway_container *con = instruction->container;
185 if (con->type == C_VIEW && 223 if (con->type == C_VIEW && !con->destroying &&
186 (con->current.view_width != instruction->state.view_width || 224 (con->current.view_width != instruction->state.view_width ||
187 con->current.view_height != instruction->state.view_height)) { 225 con->current.view_height != instruction->state.view_height)) {
188 instruction->serial = view_configure(con->sway_view, 226 instruction->serial = view_configure(con->sway_view,
@@ -191,14 +229,12 @@ void transaction_commit(struct sway_transaction *transaction) {
191 instruction->state.view_width, 229 instruction->state.view_width,
192 instruction->state.view_height); 230 instruction->state.view_height);
193 if (instruction->serial) { 231 if (instruction->serial) {
194 save_view_buffer(con->sway_view);
195 list_add(con->sway_view->instructions, instruction);
196 ++transaction->num_waiting; 232 ++transaction->num_waiting;
197 } 233 }
198 } 234 }
235 list_add(con->instructions, instruction);
199 } 236 }
200 if (!transaction->num_waiting) { 237 if (!transaction->num_waiting) {
201 // This can happen if the transaction only contains xwayland views
202 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", 238 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying",
203 transaction); 239 transaction);
204 transaction_apply(transaction); 240 transaction_apply(transaction);
@@ -210,31 +246,47 @@ void transaction_commit(struct sway_transaction *transaction) {
210 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, 246 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
211 handle_timeout, transaction); 247 handle_timeout, transaction);
212 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); 248 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS);
249
250 // The debug tree shows the pending/live tree. Here is a good place to
251 // update it, because we make a transaction every time we change the pending
252 // tree.
253 update_debug_tree();
213} 254}
214 255
215void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { 256void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) {
216 // Find the instruction 257 // Find the instruction
217 struct sway_transaction_instruction *instruction = NULL; 258 struct sway_transaction_instruction *instruction = NULL;
218 for (int i = 0; i < view->instructions->length; ++i) { 259 for (int i = 0; i < view->swayc->instructions->length; ++i) {
219 struct sway_transaction_instruction *tmp_instruction = 260 struct sway_transaction_instruction *tmp_instruction =
220 view->instructions->items[i]; 261 view->swayc->instructions->items[i];
221 if (tmp_instruction->serial == serial) { 262 if (tmp_instruction->serial == serial && !tmp_instruction->ready) {
222 instruction = tmp_instruction; 263 instruction = tmp_instruction;
223 list_del(view->instructions, i);
224 break; 264 break;
225 } 265 }
226 } 266 }
227 if (!instruction) { 267 if (!instruction) {
228 // This can happen if the view acknowledges the configure after the
229 // transaction has timed out and applied.
230 return; 268 return;
231 } 269 }
270 instruction->ready = true;
271
232 // If all views are ready, apply the transaction 272 // If all views are ready, apply the transaction
233 struct sway_transaction *transaction = instruction->transaction; 273 struct sway_transaction *transaction = instruction->transaction;
234 if (--transaction->num_waiting == 0) { 274 if (--transaction->num_waiting == 0) {
275#if !TRANSACTION_DEBUG
235 wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); 276 wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction);
236 wl_event_source_timer_update(transaction->timer, 0); 277 wl_event_source_timer_update(transaction->timer, 0);
237 transaction_apply(transaction); 278 transaction_apply(transaction);
238 transaction_destroy(transaction); 279 transaction_destroy(transaction);
280#endif
239 } 281 }
240} 282}
283
284struct wlr_texture *transaction_get_texture(struct sway_view *view) {
285 if (!view->swayc || !view->swayc->instructions->length) {
286 return view->surface->buffer->texture;
287 }
288 struct sway_transaction_instruction *instruction =
289 view->swayc->instructions->items[0];
290 return instruction->saved_buffer ?
291 instruction->saved_buffer->texture : NULL;
292}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index d22c967c..ab35b98f 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -143,16 +143,12 @@ static void _close(struct sway_view *view) {
143 } 143 }
144} 144}
145 145
146static void destroy(struct sway_view *view) { 146static void _free(struct sway_view *view) {
147 struct sway_xdg_shell_view *xdg_shell_view = 147 struct sway_xdg_shell_view *xdg_shell_view =
148 xdg_shell_view_from_view(view); 148 xdg_shell_view_from_view(view);
149 if (xdg_shell_view == NULL) { 149 if (xdg_shell_view == NULL) {
150 return; 150 return;
151 } 151 }
152 wl_list_remove(&xdg_shell_view->destroy.link);
153 wl_list_remove(&xdg_shell_view->map.link);
154 wl_list_remove(&xdg_shell_view->unmap.link);
155 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
156 free(xdg_shell_view); 152 free(xdg_shell_view);
157} 153}
158 154
@@ -164,7 +160,7 @@ static const struct sway_view_impl view_impl = {
164 .wants_floating = wants_floating, 160 .wants_floating = wants_floating,
165 .for_each_surface = for_each_surface, 161 .for_each_surface = for_each_surface,
166 .close = _close, 162 .close = _close,
167 .destroy = destroy, 163 .free = _free,
168}; 164};
169 165
170static void handle_commit(struct wl_listener *listener, void *data) { 166static void handle_commit(struct wl_listener *listener, void *data) {
@@ -173,7 +169,11 @@ static void handle_commit(struct wl_listener *listener, void *data) {
173 struct sway_view *view = &xdg_shell_view->view; 169 struct sway_view *view = &xdg_shell_view->view;
174 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 170 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
175 171
176 if (view->instructions->length) { 172 if (!view->swayc) {
173 return;
174 }
175
176 if (view->swayc->instructions->length) {
177 transaction_notify_view_ready(view, xdg_surface->configure_serial); 177 transaction_notify_view_ready(view, xdg_surface->configure_serial);
178 } 178 }
179 179
@@ -191,11 +191,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
191static void handle_unmap(struct wl_listener *listener, void *data) { 191static void handle_unmap(struct wl_listener *listener, void *data) {
192 struct sway_xdg_shell_view *xdg_shell_view = 192 struct sway_xdg_shell_view *xdg_shell_view =
193 wl_container_of(listener, xdg_shell_view, unmap); 193 wl_container_of(listener, xdg_shell_view, unmap);
194 struct sway_view *view = &xdg_shell_view->view;
195
196 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
197 return;
198 }
194 199
195 view_unmap(&xdg_shell_view->view); 200 struct sway_container *parent = view_unmap(view);
201 arrange_and_commit(parent);
196 202
197 wl_list_remove(&xdg_shell_view->commit.link); 203 wl_list_remove(&xdg_shell_view->commit.link);
198 wl_list_remove(&xdg_shell_view->new_popup.link); 204 wl_list_remove(&xdg_shell_view->new_popup.link);
205 view->surface = NULL;
199} 206}
200 207
201static void handle_map(struct wl_listener *listener, void *data) { 208static void handle_map(struct wl_listener *listener, void *data) {
@@ -230,7 +237,17 @@ static void handle_map(struct wl_listener *listener, void *data) {
230static void handle_destroy(struct wl_listener *listener, void *data) { 237static void handle_destroy(struct wl_listener *listener, void *data) {
231 struct sway_xdg_shell_view *xdg_shell_view = 238 struct sway_xdg_shell_view *xdg_shell_view =
232 wl_container_of(listener, xdg_shell_view, destroy); 239 wl_container_of(listener, xdg_shell_view, destroy);
233 view_destroy(&xdg_shell_view->view); 240 struct sway_view *view = &xdg_shell_view->view;
241 if (!sway_assert(view->swayc == NULL || view->swayc->destroying,
242 "Tried to destroy a mapped view")) {
243 return;
244 }
245 wl_list_remove(&xdg_shell_view->destroy.link);
246 wl_list_remove(&xdg_shell_view->map.link);
247 wl_list_remove(&xdg_shell_view->unmap.link);
248 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
249 view->wlr_xdg_surface = NULL;
250 view_destroy(view);
234} 251}
235 252
236static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 253static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 7ec9e6cb..76c1fa24 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -143,16 +143,12 @@ static void _close(struct sway_view *view) {
143 } 143 }
144} 144}
145 145
146static void destroy(struct sway_view *view) { 146static void _free(struct sway_view *view) {
147 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 147 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
148 xdg_shell_v6_view_from_view(view); 148 xdg_shell_v6_view_from_view(view);
149 if (xdg_shell_v6_view == NULL) { 149 if (xdg_shell_v6_view == NULL) {
150 return; 150 return;
151 } 151 }
152 wl_list_remove(&xdg_shell_v6_view->destroy.link);
153 wl_list_remove(&xdg_shell_v6_view->map.link);
154 wl_list_remove(&xdg_shell_v6_view->unmap.link);
155 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
156 free(xdg_shell_v6_view); 152 free(xdg_shell_v6_view);
157} 153}
158 154
@@ -164,7 +160,7 @@ static const struct sway_view_impl view_impl = {
164 .wants_floating = wants_floating, 160 .wants_floating = wants_floating,
165 .for_each_surface = for_each_surface, 161 .for_each_surface = for_each_surface,
166 .close = _close, 162 .close = _close,
167 .destroy = destroy, 163 .free = _free,
168}; 164};
169 165
170static void handle_commit(struct wl_listener *listener, void *data) { 166static void handle_commit(struct wl_listener *listener, void *data) {
@@ -173,7 +169,10 @@ static void handle_commit(struct wl_listener *listener, void *data) {
173 struct sway_view *view = &xdg_shell_v6_view->view; 169 struct sway_view *view = &xdg_shell_v6_view->view;
174 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; 170 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
175 171
176 if (view->instructions->length) { 172 if (!view->swayc) {
173 return;
174 }
175 if (view->swayc->instructions->length) {
177 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); 176 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
178 } 177 }
179 178
@@ -191,11 +190,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
191static void handle_unmap(struct wl_listener *listener, void *data) { 190static void handle_unmap(struct wl_listener *listener, void *data) {
192 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 191 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
193 wl_container_of(listener, xdg_shell_v6_view, unmap); 192 wl_container_of(listener, xdg_shell_v6_view, unmap);
193 struct sway_view *view = &xdg_shell_v6_view->view;
194 194
195 view_unmap(&xdg_shell_v6_view->view); 195 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
196 return;
197 }
198
199 struct sway_container *parent = view_unmap(view);
200 arrange_and_commit(parent);
196 201
197 wl_list_remove(&xdg_shell_v6_view->commit.link); 202 wl_list_remove(&xdg_shell_v6_view->commit.link);
198 wl_list_remove(&xdg_shell_v6_view->new_popup.link); 203 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
204 view->surface = NULL;
199} 205}
200 206
201static void handle_map(struct wl_listener *listener, void *data) { 207static void handle_map(struct wl_listener *listener, void *data) {
@@ -230,7 +236,13 @@ static void handle_map(struct wl_listener *listener, void *data) {
230static void handle_destroy(struct wl_listener *listener, void *data) { 236static void handle_destroy(struct wl_listener *listener, void *data) {
231 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 237 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
232 wl_container_of(listener, xdg_shell_v6_view, destroy); 238 wl_container_of(listener, xdg_shell_v6_view, destroy);
233 view_destroy(&xdg_shell_v6_view->view); 239 struct sway_view *view = &xdg_shell_v6_view->view;
240 wl_list_remove(&xdg_shell_v6_view->destroy.link);
241 wl_list_remove(&xdg_shell_v6_view->map.link);
242 wl_list_remove(&xdg_shell_v6_view->unmap.link);
243 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
244 view->wlr_xdg_surface_v6 = NULL;
245 view_destroy(view);
234} 246}
235 247
236static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 248static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 55917bf6..a1837420 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -218,19 +218,11 @@ static void _close(struct sway_view *view) {
218 wlr_xwayland_surface_close(view->wlr_xwayland_surface); 218 wlr_xwayland_surface_close(view->wlr_xwayland_surface);
219} 219}
220 220
221static void destroy(struct sway_view *view) { 221static void _free(struct sway_view *view) {
222 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); 222 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
223 if (xwayland_view == NULL) { 223 if (xwayland_view == NULL) {
224 return; 224 return;
225 } 225 }
226 wl_list_remove(&xwayland_view->destroy.link);
227 wl_list_remove(&xwayland_view->request_configure.link);
228 wl_list_remove(&xwayland_view->request_fullscreen.link);
229 wl_list_remove(&xwayland_view->set_title.link);
230 wl_list_remove(&xwayland_view->set_class.link);
231 wl_list_remove(&xwayland_view->set_window_type.link);
232 wl_list_remove(&xwayland_view->map.link);
233 wl_list_remove(&xwayland_view->unmap.link);
234 free(xwayland_view); 226 free(xwayland_view);
235} 227}
236 228
@@ -242,7 +234,7 @@ static const struct sway_view_impl view_impl = {
242 .set_fullscreen = set_fullscreen, 234 .set_fullscreen = set_fullscreen,
243 .wants_floating = wants_floating, 235 .wants_floating = wants_floating,
244 .close = _close, 236 .close = _close,
245 .destroy = destroy, 237 .free = _free,
246}; 238};
247 239
248static void handle_commit(struct wl_listener *listener, void *data) { 240static void handle_commit(struct wl_listener *listener, void *data) {
@@ -254,7 +246,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
254 // Don't allow xwayland views to do resize or reposition themselves if 246 // Don't allow xwayland views to do resize or reposition themselves if
255 // they're involved in a transaction. Once the transaction has finished 247 // they're involved in a transaction. Once the transaction has finished
256 // they'll apply the next time a commit happens. 248 // they'll apply the next time a commit happens.
257 if (view->instructions->length) { 249 if (view->swayc && view->swayc->instructions->length) {
258 if (view->swayc && container_is_floating(view->swayc)) { 250 if (view->swayc && container_is_floating(view->swayc)) {
259 view_update_size(view, xsurface->width, xsurface->height); 251 view_update_size(view, xsurface->width, xsurface->height);
260 } else { 252 } else {
@@ -268,8 +260,17 @@ static void handle_commit(struct wl_listener *listener, void *data) {
268static void handle_unmap(struct wl_listener *listener, void *data) { 260static void handle_unmap(struct wl_listener *listener, void *data) {
269 struct sway_xwayland_view *xwayland_view = 261 struct sway_xwayland_view *xwayland_view =
270 wl_container_of(listener, xwayland_view, unmap); 262 wl_container_of(listener, xwayland_view, unmap);
263 struct sway_view *view = &xwayland_view->view;
264
265 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
266 return;
267 }
268
269 struct sway_container *parent = view_unmap(view);
270 arrange_and_commit(parent);
271
271 wl_list_remove(&xwayland_view->commit.link); 272 wl_list_remove(&xwayland_view->commit.link);
272 view_unmap(&xwayland_view->view); 273 view->surface = NULL;
273} 274}
274 275
275static void handle_map(struct wl_listener *listener, void *data) { 276static void handle_map(struct wl_listener *listener, void *data) {
@@ -293,12 +294,30 @@ static void handle_map(struct wl_listener *listener, void *data) {
293 if (xsurface->fullscreen) { 294 if (xsurface->fullscreen) {
294 view_set_fullscreen(view, true); 295 view_set_fullscreen(view, true);
295 } 296 }
296 arrange_and_commit(view->swayc); 297 arrange_and_commit(view->swayc->parent);
297} 298}
298 299
299static void handle_destroy(struct wl_listener *listener, void *data) { 300static void handle_destroy(struct wl_listener *listener, void *data) {
300 struct sway_xwayland_view *xwayland_view = 301 struct sway_xwayland_view *xwayland_view =
301 wl_container_of(listener, xwayland_view, destroy); 302 wl_container_of(listener, xwayland_view, destroy);
303 struct sway_view *view = &xwayland_view->view;
304
305 if (view->surface) {
306 struct sway_container *parent = view_unmap(view);
307 arrange_and_commit(parent);
308
309 wl_list_remove(&xwayland_view->commit.link);
310 view->surface = NULL;
311 }
312
313 wl_list_remove(&xwayland_view->destroy.link);
314 wl_list_remove(&xwayland_view->request_configure.link);
315 wl_list_remove(&xwayland_view->request_fullscreen.link);
316 wl_list_remove(&xwayland_view->set_title.link);
317 wl_list_remove(&xwayland_view->set_class.link);
318 wl_list_remove(&xwayland_view->set_window_type.link);
319 wl_list_remove(&xwayland_view->map.link);
320 wl_list_remove(&xwayland_view->unmap.link);
302 view_destroy(&xwayland_view->view); 321 view_destroy(&xwayland_view->view);
303} 322}
304 323
diff --git a/sway/main.c b/sway/main.c
index a7e808ad..a83660d5 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -34,6 +34,7 @@ struct sway_server server;
34void sway_terminate(int exit_code) { 34void sway_terminate(int exit_code) {
35 terminate_request = true; 35 terminate_request = true;
36 exit_value = exit_code; 36 exit_value = exit_code;
37 server.terminating = true;
37 wl_display_terminate(server.wl_display); 38 wl_display_terminate(server.wl_display);
38} 39}
39 40
diff --git a/sway/server.c b/sway/server.c
index 824b1d8e..a13f2c3a 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -19,6 +19,7 @@
19#include <wlr/util/log.h> 19#include <wlr/util/log.h>
20// TODO WLR: make Xwayland optional 20// TODO WLR: make Xwayland optional
21#include <wlr/xwayland.h> 21#include <wlr/xwayland.h>
22#include "list.h"
22#include "sway/config.h" 23#include "sway/config.h"
23#include "sway/input/input-manager.h" 24#include "sway/input/input-manager.h"
24#include "sway/server.h" 25#include "sway/server.h"
@@ -105,6 +106,8 @@ bool server_init(struct sway_server *server) {
105 return false; 106 return false;
106 } 107 }
107 108
109 server->destroying_containers = create_list();
110
108 input_manager = input_manager_create(server); 111 input_manager = input_manager_create(server);
109 return true; 112 return true;
110} 113}
@@ -112,6 +115,7 @@ bool server_init(struct sway_server *server) {
112void server_fini(struct sway_server *server) { 115void server_fini(struct sway_server *server) {
113 // TODO: free sway-specific resources 116 // TODO: free sway-specific resources
114 wl_display_destroy(server->wl_display); 117 wl_display_destroy(server->wl_display);
118 list_free(server->destroying_containers);
115} 119}
116 120
117void server_run(struct sway_server *server) { 121void server_run(struct sway_server *server) {
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index ac99c5df..cb3f8ba2 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -144,6 +144,19 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
144 } 144 }
145} 145}
146 146
147/**
148 * If a container has been deleted from the pending tree state, we must add it
149 * to the transaction so it can be freed afterwards. To do this, we iterate the
150 * server's destroying_containers list and add all of them. We may add more than
151 * what we need to, but this is easy and has no negative consequences.
152 */
153static void add_deleted_containers(struct sway_transaction *transaction) {
154 for (int i = 0; i < server.destroying_containers->length; ++i) {
155 struct sway_container *child = server.destroying_containers->items[i];
156 transaction_add_container(transaction, child);
157 }
158}
159
147static void arrange_children_of(struct sway_container *parent, 160static void arrange_children_of(struct sway_container *parent,
148 struct sway_transaction *transaction); 161 struct sway_transaction *transaction);
149 162
@@ -158,6 +171,7 @@ static void arrange_floating(struct sway_container *floating,
158 } 171 }
159 transaction_add_container(transaction, floater); 172 transaction_add_container(transaction, floater);
160 } 173 }
174 transaction_add_container(transaction, floating);
161} 175}
162 176
163static void arrange_children_of(struct sway_container *parent, 177static void arrange_children_of(struct sway_container *parent,
@@ -290,7 +304,16 @@ void arrange_windows(struct sway_container *container,
290 case C_TYPES: 304 case C_TYPES:
291 break; 305 break;
292 } 306 }
293 transaction_add_damage(transaction, container_get_box(container)); 307 // Add damage for whatever container arrange_windows() was called with,
308 // unless it was called with the special floating container, in which case
309 // we'll damage the entire output.
310 if (container->type == C_CONTAINER && container->layout == L_FLOATING) {
311 struct sway_container *output = container_parent(container, C_OUTPUT);
312 transaction_add_damage(transaction, container_get_box(output));
313 } else {
314 transaction_add_damage(transaction, container_get_box(container));
315 }
316 add_deleted_containers(transaction);
294} 317}
295 318
296void arrange_and_commit(struct sway_container *container) { 319void arrange_and_commit(struct sway_container *container) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index b071f394..484d26a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -16,7 +16,6 @@
16#include "sway/ipc-server.h" 16#include "sway/ipc-server.h"
17#include "sway/output.h" 17#include "sway/output.h"
18#include "sway/server.h" 18#include "sway/server.h"
19#include "sway/tree/arrange.h"
20#include "sway/tree/layout.h" 19#include "sway/tree/layout.h"
21#include "sway/tree/view.h" 20#include "sway/tree/view.h"
22#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
@@ -113,10 +112,11 @@ struct sway_container *container_create(enum sway_container_type type) {
113 c->layout = L_NONE; 112 c->layout = L_NONE;
114 c->type = type; 113 c->type = type;
115 c->alpha = 1.0f; 114 c->alpha = 1.0f;
115 c->instructions = create_list();
116 116
117 if (type != C_VIEW) { 117 if (type != C_VIEW) {
118 c->children = create_list(); 118 c->children = create_list();
119 //c->pending.children = create_list(); 119 c->current.children = create_list();
120 } 120 }
121 121
122 wl_signal_init(&c->events.destroy); 122 wl_signal_init(&c->events.destroy);
@@ -133,43 +133,68 @@ struct sway_container *container_create(enum sway_container_type type) {
133 return c; 133 return c;
134} 134}
135 135
136static void _container_destroy(struct sway_container *cont) { 136static void container_workspace_free(struct sway_workspace *ws) {
137 if (cont == NULL) { 137 list_foreach(ws->output_priority, free);
138 return; 138 list_free(ws->output_priority);
139 } 139 ws->floating->destroying = true;
140 140 container_free(ws->floating);
141 wl_signal_emit(&cont->events.destroy, cont); 141 free(ws);
142}
142 143
143 struct sway_container *parent = cont->parent; 144void container_free(struct sway_container *cont) {
144 if (cont->children != NULL && cont->children->length) { 145 if (!sway_assert(cont->destroying,
145 // remove children until there are no more, container_destroy calls 146 "Tried to free container which wasn't marked as destroying")) {
146 // container_remove_child, which removes child from this container 147 return;
147 while (cont->children != NULL && cont->children->length > 0) {
148 struct sway_container *child = cont->children->items[0];
149 ipc_event_window(child, "close");
150 container_remove_child(child);
151 _container_destroy(child);
152 }
153 }
154 if (cont->marks) {
155 list_foreach(cont->marks, free);
156 list_free(cont->marks);
157 }
158 if (parent) {
159 parent = container_remove_child(cont);
160 } 148 }
161 if (cont->name) { 149 if (!sway_assert(cont->instructions->length == 0,
162 free(cont->name); 150 "Tried to free container with pending instructions")) {
151 return;
163 } 152 }
164 153 free(cont->name);
165 wlr_texture_destroy(cont->title_focused); 154 wlr_texture_destroy(cont->title_focused);
166 wlr_texture_destroy(cont->title_focused_inactive); 155 wlr_texture_destroy(cont->title_focused_inactive);
167 wlr_texture_destroy(cont->title_unfocused); 156 wlr_texture_destroy(cont->title_unfocused);
168 wlr_texture_destroy(cont->title_urgent); 157 wlr_texture_destroy(cont->title_urgent);
169 158
159 for (int i = 0; i < server.destroying_containers->length; ++i) {
160 if (server.destroying_containers->items[i] == cont) {
161 list_del(server.destroying_containers, i);
162 break;
163 }
164 }
165
166 list_free(cont->instructions);
170 list_free(cont->children); 167 list_free(cont->children);
171 //list_free(cont->pending.children); 168 list_free(cont->current.children);
172 cont->children = NULL; 169
170 switch (cont->type) {
171 case C_ROOT:
172 break;
173 case C_OUTPUT:
174 cont->sway_output->swayc = NULL;
175 break;
176 case C_WORKSPACE:
177 container_workspace_free(cont->sway_workspace);
178 break;
179 case C_CONTAINER:
180 break;
181 case C_VIEW:
182 {
183 struct sway_view *view = cont->sway_view;
184 view->swayc = NULL;
185 free(view->title_format);
186 view->title_format = NULL;
187
188 if (view->destroying) {
189 view_free(view);
190 }
191 }
192 break;
193 case C_TYPES:
194 sway_assert(false, "Didn't expect to see C_TYPES here");
195 break;
196 }
197
173 free(cont); 198 free(cont);
174} 199}
175 200
@@ -186,7 +211,6 @@ static struct sway_container *container_workspace_destroy(
186 } 211 }
187 212
188 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 213 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
189 ipc_event_window(workspace, "close");
190 214
191 struct sway_container *parent = workspace->parent; 215 struct sway_container *parent = workspace->parent;
192 if (!workspace_is_empty(workspace) && output) { 216 if (!workspace_is_empty(workspace) && output) {
@@ -209,25 +233,6 @@ static struct sway_container *container_workspace_destroy(
209 container_move_to(floating->children->items[i], 233 container_move_to(floating->children->items[i],
210 new_workspace->sway_workspace->floating); 234 new_workspace->sway_workspace->floating);
211 } 235 }
212 arrange_and_commit(new_workspace);
213 }
214
215 struct sway_workspace *sway_workspace = workspace->sway_workspace;
216
217 // This emits the destroy event and also destroys the swayc.
218 _container_destroy(workspace);
219
220 // Clean up the floating container
221 sway_workspace->floating->parent = NULL;
222 _container_destroy(sway_workspace->floating);
223
224 list_foreach(sway_workspace->output_priority, free);
225 list_free(sway_workspace->output_priority);
226
227 free(sway_workspace);
228
229 if (output) {
230 output_damage_whole(output->sway_output);
231 } 236 }
232 237
233 return parent; 238 return parent;
@@ -266,14 +271,13 @@ static struct sway_container *container_output_destroy(
266 container_add_child(new_output, workspace); 271 container_add_child(new_output, workspace);
267 ipc_event_workspace(workspace, NULL, "move"); 272 ipc_event_workspace(workspace, NULL, "move");
268 } else { 273 } else {
269 container_workspace_destroy(workspace); 274 container_destroy(workspace);
270 } 275 }
271 276
272 container_sort_workspaces(new_output); 277 container_sort_workspaces(new_output);
273 } 278 }
274 } 279 }
275 } 280 }
276 arrange_and_commit(&root_container);
277 281
278 wl_list_remove(&output->sway_output->mode.link); 282 wl_list_remove(&output->sway_output->mode.link);
279 wl_list_remove(&output->sway_output->transform.link); 283 wl_list_remove(&output->sway_output->transform.link);
@@ -285,12 +289,8 @@ static struct sway_container *container_output_destroy(
285 output->sway_output->swayc = NULL; 289 output->sway_output->swayc = NULL;
286 290
287 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 291 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
288 _container_destroy(output);
289 return &root_container;
290}
291 292
292static void container_root_finish(struct sway_container *con) { 293 return &root_container;
293 wlr_log(L_ERROR, "TODO: destroy the root container");
294} 294}
295 295
296bool container_reap_empty(struct sway_container *con) { 296bool container_reap_empty(struct sway_container *con) {
@@ -306,13 +306,13 @@ bool container_reap_empty(struct sway_container *con) {
306 case C_WORKSPACE: 306 case C_WORKSPACE:
307 if (!workspace_is_visible(con) && workspace_is_empty(con)) { 307 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
308 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 308 wlr_log(L_DEBUG, "Destroying workspace via reaper");
309 container_workspace_destroy(con); 309 container_destroy(con);
310 return true; 310 return true;
311 } 311 }
312 break; 312 break;
313 case C_CONTAINER: 313 case C_CONTAINER:
314 if (con->children->length == 0) { 314 if (con->children->length == 0) {
315 _container_destroy(con); 315 container_destroy(con);
316 return true; 316 return true;
317 } 317 }
318 case C_VIEW: 318 case C_VIEW:
@@ -349,46 +349,48 @@ struct sway_container *container_flatten(struct sway_container *container) {
349 return container; 349 return container;
350} 350}
351 351
352/**
353 * container_destroy() is the first step in destroying a container. We'll emit
354 * events, detach it from the tree and mark it as destroying. The container will
355 * remain in memory until it's no longer used by a transaction, then it will be
356 * freed via container_free().
357 */
352struct sway_container *container_destroy(struct sway_container *con) { 358struct sway_container *container_destroy(struct sway_container *con) {
353 if (con == NULL) { 359 if (con == NULL) {
354 return NULL; 360 return NULL;
355 } 361 }
362 if (con->destroying) {
363 return NULL;
364 }
356 365
357 struct sway_container *parent = con->parent; 366 // The below functions move their children to somewhere else.
367 if (con->type == C_OUTPUT) {
368 container_output_destroy(con);
369 } else if (con->type == C_WORKSPACE) {
370 // Workspaces will refuse to be destroyed if they're the last workspace
371 // on their output.
372 if (!container_workspace_destroy(con)) {
373 return NULL;
374 }
375 }
358 376
359 switch (con->type) { 377 // At this point the container being destroyed shouldn't have any children
360 case C_ROOT: 378 // unless sway is terminating.
361 container_root_finish(con); 379 if (!server.terminating) {
362 break; 380 if (!sway_assert(!con->children || con->children->length == 0,
363 case C_OUTPUT: 381 "Didn't expect to see children here")) {
364 // dont try to reap the root after this 382 return NULL;
365 container_output_destroy(con); 383 }
366 break;
367 case C_WORKSPACE:
368 // dont try to reap the output after this
369 container_workspace_destroy(con);
370 break;
371 case C_CONTAINER:
372 if (con->children->length) {
373 for (int i = 0; i < con->children->length; ++i) {
374 struct sway_container *child = con->children->items[0];
375 ipc_event_window(child, "close");
376 container_remove_child(child);
377 container_add_child(parent, child);
378 }
379 }
380 ipc_event_window(con, "close");
381 _container_destroy(con);
382 break;
383 case C_VIEW:
384 _container_destroy(con);
385 break;
386 case C_TYPES:
387 wlr_log(L_ERROR, "container_destroy called on an invalid "
388 "container");
389 break;
390 } 384 }
391 385
386 wl_signal_emit(&con->events.destroy, con);
387 ipc_event_window(con, "close");
388
389 struct sway_container *parent = container_remove_child(con);
390
391 con->destroying = true;
392 list_add(server.destroying_containers, con);
393
392 return container_reap_empty_recursive(parent); 394 return container_reap_empty_recursive(parent);
393} 395}
394 396
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 3724361d..14631ad4 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -30,7 +30,9 @@ void layout_init(void) {
30 root_container.type = C_ROOT; 30 root_container.type = C_ROOT;
31 root_container.layout = L_NONE; 31 root_container.layout = L_NONE;
32 root_container.name = strdup("root"); 32 root_container.name = strdup("root");
33 root_container.instructions = create_list();
33 root_container.children = create_list(); 34 root_container.children = create_list();
35 root_container.current.children = create_list();
34 wl_signal_init(&root_container.events.destroy); 36 wl_signal_init(&root_container.events.destroy);
35 37
36 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); 38 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 8af319d5..e2927cdb 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -29,7 +29,6 @@ static void restore_workspaces(struct sway_container *output) {
29 } 29 }
30 30
31 container_sort_workspaces(output); 31 container_sort_workspaces(output);
32 arrange_and_commit(&root_container);
33} 32}
34 33
35struct sway_container *output_create( 34struct sway_container *output_create(
@@ -66,7 +65,6 @@ struct sway_container *output_create(
66 65
67 struct sway_container *output = container_create(C_OUTPUT); 66 struct sway_container *output = container_create(C_OUTPUT);
68 output->sway_output = sway_output; 67 output->sway_output = sway_output;
69 sway_output->swayc = output;
70 output->name = strdup(name); 68 output->name = strdup(name);
71 if (output->name == NULL) { 69 if (output->name == NULL) {
72 container_destroy(output); 70 container_destroy(output);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 658a94e8..cb36f123 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -25,47 +25,60 @@ void view_init(struct sway_view *view, enum sway_view_type type,
25 view->impl = impl; 25 view->impl = impl;
26 view->executed_criteria = create_list(); 26 view->executed_criteria = create_list();
27 view->marks = create_list(); 27 view->marks = create_list();
28 view->instructions = create_list();
29 wl_signal_init(&view->events.unmap); 28 wl_signal_init(&view->events.unmap);
30} 29}
31 30
32void view_destroy(struct sway_view *view) { 31void view_free(struct sway_view *view) {
33 if (view == NULL) { 32 if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) {
34 return; 33 return;
35 } 34 }
36 35 if (!sway_assert(view->destroying,
37 if (view->surface != NULL) { 36 "Tried to free view which wasn't marked as destroying")) {
38 view_unmap(view); 37 return;
39 } 38 }
40 39 if (!sway_assert(view->swayc == NULL,
41 if (!sway_assert(view->instructions->length == 0, 40 "Tried to free view which still has a swayc "
42 "Tried to destroy view with pending instructions")) { 41 "(might have a pending transaction?)")) {
43 return; 42 return;
44 } 43 }
45
46 list_free(view->executed_criteria); 44 list_free(view->executed_criteria);
47 45
48 for (int i = 0; i < view->marks->length; ++i) { 46 list_foreach(view->marks, free);
49 free(view->marks->items[i]);
50 }
51 list_free(view->marks); 47 list_free(view->marks);
52 48
53 list_free(view->instructions);
54
55 wlr_texture_destroy(view->marks_focused); 49 wlr_texture_destroy(view->marks_focused);
56 wlr_texture_destroy(view->marks_focused_inactive); 50 wlr_texture_destroy(view->marks_focused_inactive);
57 wlr_texture_destroy(view->marks_unfocused); 51 wlr_texture_destroy(view->marks_unfocused);
58 wlr_texture_destroy(view->marks_urgent); 52 wlr_texture_destroy(view->marks_urgent);
59 53
60 container_destroy(view->swayc); 54 if (view->impl->free) {
61 55 view->impl->free(view);
62 if (view->impl->destroy) {
63 view->impl->destroy(view);
64 } else { 56 } else {
65 free(view); 57 free(view);
66 } 58 }
67} 59}
68 60
61/**
62 * The view may or may not be involved in a transaction. For example, a view may
63 * unmap then attempt to destroy itself before we've applied the new layout. If
64 * an unmapping view is still involved in a transaction then it'll still have a
65 * swayc.
66 *
67 * If there's no transaction we can simply free the view. Otherwise the
68 * destroying flag will make the view get freed when the transaction is
69 * finished.
70 */
71void view_destroy(struct sway_view *view) {
72 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
73 return;
74 }
75 view->destroying = true;
76
77 if (!view->swayc) {
78 view_free(view);
79 }
80}
81
69const char *view_get_title(struct sway_view *view) { 82const char *view_get_title(struct sway_view *view) {
70 if (view->impl->get_string_prop) { 83 if (view->impl->get_string_prop) {
71 return view->impl->get_string_prop(view, VIEW_PROP_TITLE); 84 return view->impl->get_string_prop(view, VIEW_PROP_TITLE);
@@ -356,6 +369,9 @@ static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
356 369
357void view_for_each_surface(struct sway_view *view, 370void view_for_each_surface(struct sway_view *view,
358 wlr_surface_iterator_func_t iterator, void *user_data) { 371 wlr_surface_iterator_func_t iterator, void *user_data) {
372 if (!view->surface) {
373 return;
374 }
359 if (view->impl->for_each_surface) { 375 if (view->impl->for_each_surface) {
360 view->impl->for_each_surface(view, iterator, user_data); 376 view->impl->for_each_surface(view, iterator, user_data);
361 } else { 377 } else {
@@ -523,11 +539,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
523 view_handle_container_reparent(&view->container_reparent, NULL); 539 view_handle_container_reparent(&view->container_reparent, NULL);
524} 540}
525 541
526void view_unmap(struct sway_view *view) { 542struct sway_container *view_unmap(struct sway_view *view) {
527 if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
528 return;
529 }
530
531 wl_signal_emit(&view->events.unmap, view); 543 wl_signal_emit(&view->events.unmap, view);
532 544
533 if (view->is_fullscreen) { 545 if (view->is_fullscreen) {
@@ -535,22 +547,10 @@ void view_unmap(struct sway_view *view) {
535 ws->sway_workspace->fullscreen = NULL; 547 ws->sway_workspace->fullscreen = NULL;
536 } 548 }
537 549
538 container_damage_whole(view->swayc);
539
540 wl_list_remove(&view->surface_new_subsurface.link); 550 wl_list_remove(&view->surface_new_subsurface.link);
541 wl_list_remove(&view->container_reparent.link); 551 wl_list_remove(&view->container_reparent.link);
542 552
543 struct sway_container *parent = container_destroy(view->swayc); 553 return container_destroy(view->swayc);
544
545 view->swayc = NULL;
546 view->surface = NULL;
547
548 if (view->title_format) {
549 free(view->title_format);
550 view->title_format = NULL;
551 }
552
553 arrange_and_commit(parent);
554} 554}
555 555
556void view_update_position(struct sway_view *view, double lx, double ly) { 556void view_update_position(struct sway_view *view, double lx, double ly) {
@@ -924,7 +924,7 @@ void view_update_marks_textures(struct sway_view *view) {
924} 924}
925 925
926bool view_is_visible(struct sway_view *view) { 926bool view_is_visible(struct sway_view *view) {
927 if (!view->swayc) { 927 if (!view->swayc || view->swayc->destroying) {
928 return false; 928 return false;
929 } 929 }
930 struct sway_container *workspace = 930 struct sway_container *workspace =
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index ead752ad..5eb4be0f 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -430,6 +430,9 @@ bool workspace_switch(struct sway_container *workspace) {
430} 430}
431 431
432bool workspace_is_visible(struct sway_container *ws) { 432bool workspace_is_visible(struct sway_container *ws) {
433 if (ws->destroying) {
434 return false;
435 }
433 struct sway_container *output = container_parent(ws, C_OUTPUT); 436 struct sway_container *output = container_parent(ws, C_OUTPUT);
434 struct sway_seat *seat = input_manager_current_seat(input_manager); 437 struct sway_seat *seat = input_manager_current_seat(input_manager);
435 struct sway_container *focus = seat_get_focus_inactive(seat, output); 438 struct sway_container *focus = seat_get_focus_inactive(seat, output);