diff options
author | 2018-06-23 16:24:11 +1000 | |
---|---|---|
committer | 2018-06-23 16:24:11 +1000 | |
commit | 38398e2d77d57dc06b67ec88a54091c897915602 (patch) | |
tree | c80935807865fd96ab7d037070287d4dfaba1863 | |
parent | Preserve buffers during transactions (diff) | |
download | sway-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.
-rw-r--r-- | include/sway/desktop/transaction.h | 9 | ||||
-rw-r--r-- | include/sway/server.h | 7 | ||||
-rw-r--r-- | include/sway/tree/container.h | 16 | ||||
-rw-r--r-- | include/sway/tree/view.h | 18 | ||||
-rw-r--r-- | sway/commands/split.c | 2 | ||||
-rw-r--r-- | sway/desktop/output.c | 293 | ||||
-rw-r--r-- | sway/desktop/transaction.c | 176 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 35 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 30 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 45 | ||||
-rw-r--r-- | sway/main.c | 1 | ||||
-rw-r--r-- | sway/server.c | 4 | ||||
-rw-r--r-- | sway/tree/arrange.c | 25 | ||||
-rw-r--r-- | sway/tree/container.c | 184 | ||||
-rw-r--r-- | sway/tree/layout.c | 2 | ||||
-rw-r--r-- | sway/tree/output.c | 2 | ||||
-rw-r--r-- | sway/tree/view.c | 76 | ||||
-rw-r--r-- | sway/tree/workspace.c | 3 |
18 files changed, 542 insertions, 386 deletions
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 5aff28e9..d6adc609 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h | |||
@@ -1,5 +1,6 @@ | |||
1 | #ifndef _SWAY_TRANSACTION_H | 1 | #ifndef _SWAY_TRANSACTION_H |
2 | #define _SWAY_TRANSACTION_H | 2 | #define _SWAY_TRANSACTION_H |
3 | #include <wlr/render/wlr_texture.h> | ||
3 | #include "sway/tree/container.h" | 4 | #include "sway/tree/container.h" |
4 | 5 | ||
5 | /** | 6 | /** |
@@ -48,4 +49,12 @@ void transaction_commit(struct sway_transaction *transaction); | |||
48 | */ | 49 | */ |
49 | void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); | 50 | void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); |
50 | 51 | ||
52 | /** | ||
53 | * Get the texture that should be rendered for a view. | ||
54 | * | ||
55 | * In most cases this will return the normal live texture for a view, but if the | ||
56 | * view is in a transaction then it'll return a saved texture. | ||
57 | */ | ||
58 | struct wlr_texture *transaction_get_texture(struct sway_view *view); | ||
59 | |||
51 | #endif | 60 | #endif |
diff --git a/include/sway/server.h b/include/sway/server.h index 65d96e7a..f5f88a5a 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <wlr/render/wlr_renderer.h> | 12 | #include <wlr/render/wlr_renderer.h> |
13 | // TODO WLR: make Xwayland optional | 13 | // TODO WLR: make Xwayland optional |
14 | #include <wlr/xwayland.h> | 14 | #include <wlr/xwayland.h> |
15 | #include "list.h" | ||
15 | 16 | ||
16 | struct sway_server { | 17 | struct sway_server { |
17 | struct wl_display *wl_display; | 18 | struct wl_display *wl_display; |
@@ -43,6 +44,12 @@ struct sway_server { | |||
43 | 44 | ||
44 | struct wlr_wl_shell *wl_shell; | 45 | struct wlr_wl_shell *wl_shell; |
45 | struct wl_listener wl_shell_surface; | 46 | struct wl_listener wl_shell_surface; |
47 | |||
48 | bool terminating; | ||
49 | |||
50 | // When a view is being destroyed and is waiting for a transaction to | ||
51 | // complete it will be stored here. | ||
52 | list_t *destroying_containers; | ||
46 | }; | 53 | }; |
47 | 54 | ||
48 | struct sway_server server; | 55 | struct sway_server server; |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index f4e978ea..7e78cbef 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -65,8 +65,8 @@ struct sway_container_state { | |||
65 | double gaps_inner; | 65 | double gaps_inner; |
66 | double gaps_outer; | 66 | double gaps_outer; |
67 | 67 | ||
68 | //struct sway_container *parent; | 68 | struct sway_container *parent; |
69 | //list_t *children; | 69 | list_t *children; |
70 | 70 | ||
71 | // View properties | 71 | // View properties |
72 | double view_x, view_y; | 72 | double view_x, view_y; |
@@ -79,6 +79,10 @@ struct sway_container_state { | |||
79 | bool border_bottom; | 79 | bool border_bottom; |
80 | bool border_left; | 80 | bool border_left; |
81 | bool border_right; | 81 | bool border_right; |
82 | |||
83 | // Workspace properties | ||
84 | struct sway_view *ws_fullscreen; | ||
85 | struct sway_container *ws_floating; | ||
82 | }; | 86 | }; |
83 | 87 | ||
84 | struct sway_container { | 88 | struct sway_container { |
@@ -128,8 +132,6 @@ struct sway_container { | |||
128 | 132 | ||
129 | struct sway_container *parent; | 133 | struct sway_container *parent; |
130 | 134 | ||
131 | list_t *marks; // list of char* | ||
132 | |||
133 | float alpha; | 135 | float alpha; |
134 | 136 | ||
135 | struct wlr_texture *title_focused; | 137 | struct wlr_texture *title_focused; |
@@ -138,6 +140,10 @@ struct sway_container { | |||
138 | struct wlr_texture *title_urgent; | 140 | struct wlr_texture *title_urgent; |
139 | size_t title_height; | 141 | size_t title_height; |
140 | 142 | ||
143 | list_t *instructions; // struct sway_transaction_instruction * | ||
144 | |||
145 | bool destroying; | ||
146 | |||
141 | struct { | 147 | struct { |
142 | struct wl_signal destroy; | 148 | struct wl_signal destroy; |
143 | // Raised after the tree updates, but before arrange_windows | 149 | // Raised after the tree updates, but before arrange_windows |
@@ -181,6 +187,8 @@ struct sway_container *workspace_create(struct sway_container *output, | |||
181 | struct sway_container *container_view_create( | 187 | struct sway_container *container_view_create( |
182 | struct sway_container *sibling, struct sway_view *sway_view); | 188 | struct sway_container *sibling, struct sway_view *sway_view); |
183 | 189 | ||
190 | void container_free(struct sway_container *cont); | ||
191 | |||
184 | struct sway_container *container_destroy(struct sway_container *container); | 192 | struct sway_container *container_destroy(struct sway_container *container); |
185 | 193 | ||
186 | struct sway_container *container_close(struct sway_container *container); | 194 | struct sway_container *container_close(struct sway_container *container); |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index fc4c8df9..5a615b43 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -37,7 +37,7 @@ struct sway_view_impl { | |||
37 | void (*for_each_surface)(struct sway_view *view, | 37 | void (*for_each_surface)(struct sway_view *view, |
38 | wlr_surface_iterator_func_t iterator, void *user_data); | 38 | wlr_surface_iterator_func_t iterator, void *user_data); |
39 | void (*close)(struct sway_view *view); | 39 | void (*close)(struct sway_view *view); |
40 | void (*destroy)(struct sway_view *view); | 40 | void (*free)(struct sway_view *view); |
41 | }; | 41 | }; |
42 | 42 | ||
43 | struct sway_view { | 43 | struct sway_view { |
@@ -68,15 +68,10 @@ struct sway_view { | |||
68 | bool border_left; | 68 | bool border_left; |
69 | bool border_right; | 69 | bool border_right; |
70 | 70 | ||
71 | bool destroying; | ||
72 | |||
71 | list_t *executed_criteria; // struct criteria * | 73 | list_t *executed_criteria; // struct criteria * |
72 | list_t *marks; // char * | 74 | list_t *marks; // char * |
73 | list_t *instructions; // struct sway_transaction_instruction * | ||
74 | |||
75 | // If saved_buffer is set, the main surface of the view will render this | ||
76 | // buffer/texture instead of its own. This is used while waiting for | ||
77 | // transactions to complete. | ||
78 | struct wlr_buffer *saved_buffer; | ||
79 | int saved_surface_width, saved_surface_height; | ||
80 | 75 | ||
81 | struct wlr_texture *marks_focused; | 76 | struct wlr_texture *marks_focused; |
82 | struct wlr_texture *marks_focused_inactive; | 77 | struct wlr_texture *marks_focused_inactive; |
@@ -244,11 +239,16 @@ void view_for_each_surface(struct sway_view *view, | |||
244 | void view_init(struct sway_view *view, enum sway_view_type type, | 239 | void view_init(struct sway_view *view, enum sway_view_type type, |
245 | const struct sway_view_impl *impl); | 240 | const struct sway_view_impl *impl); |
246 | 241 | ||
242 | void view_free(struct sway_view *view); | ||
243 | |||
247 | void view_destroy(struct sway_view *view); | 244 | void view_destroy(struct sway_view *view); |
248 | 245 | ||
249 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); | 246 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); |
250 | 247 | ||
251 | void view_unmap(struct sway_view *view); | 248 | /** |
249 | * Unmap the view and return the surviving parent (after reaping). | ||
250 | */ | ||
251 | struct sway_container *view_unmap(struct sway_view *view); | ||
252 | 252 | ||
253 | void view_update_position(struct sway_view *view, double lx, double ly); | 253 | void view_update_position(struct sway_view *view, double lx, double ly); |
254 | 254 | ||
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 | |||
113 | static 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 | ||
353 | static 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 | */ |
400 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | 391 | static 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, | |||
636 | static void render_top_border(struct sway_output *output, | 629 | static 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, | |||
711 | static void render_container_tabbed(struct sway_output *output, | 708 | static 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, | |||
772 | static void render_container_stacked(struct sway_output *output, | 770 | static 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 | ||
878 | static void render_floating(struct sway_output *soutput, | 876 | static 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 | |||
21 | struct sway_transaction { | 29 | struct 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 | ||
35 | struct sway_transaction *transaction_create() { | 45 | struct sway_transaction *transaction_create() { |
@@ -40,44 +50,55 @@ struct sway_transaction *transaction_create() { | |||
40 | return transaction; | 50 | return transaction; |
41 | } | 51 | } |
42 | 52 | ||
53 | static 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 | |||
61 | static 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 | |||
43 | static void transaction_destroy(struct sway_transaction *transaction) { | 73 | static 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 | ||
72 | void transaction_add_container(struct sway_transaction *transaction, | 100 | static 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 | |||
137 | static 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 | |||
149 | void 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 | ||
116 | static 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 | |||
126 | static 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 | */ |
141 | static void transaction_apply(struct sway_transaction *transaction) { | 177 | static 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 | ||
168 | static int handle_timeout(void *data) { | 206 | static 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 | ||
215 | void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { | 256 | void 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 | |||
284 | struct 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 | ||
146 | static void destroy(struct sway_view *view) { | 146 | static 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 | ||
170 | static void handle_commit(struct wl_listener *listener, void *data) { | 166 | static 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) { | |||
191 | static void handle_unmap(struct wl_listener *listener, void *data) { | 191 | static 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 | ||
201 | static void handle_map(struct wl_listener *listener, void *data) { | 208 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -230,7 +237,17 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
230 | static void handle_destroy(struct wl_listener *listener, void *data) { | 237 | static 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 | ||
236 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 253 | static 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 | ||
146 | static void destroy(struct sway_view *view) { | 146 | static 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 | ||
170 | static void handle_commit(struct wl_listener *listener, void *data) { | 166 | static 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) { | |||
191 | static void handle_unmap(struct wl_listener *listener, void *data) { | 190 | static 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 | ||
201 | static void handle_map(struct wl_listener *listener, void *data) { | 207 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -230,7 +236,13 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
230 | static void handle_destroy(struct wl_listener *listener, void *data) { | 236 | static 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 | ||
236 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 248 | static 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 | ||
221 | static void destroy(struct sway_view *view) { | 221 | static 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 | ||
248 | static void handle_commit(struct wl_listener *listener, void *data) { | 240 | static 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) { | |||
268 | static void handle_unmap(struct wl_listener *listener, void *data) { | 260 | static 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 | ||
275 | static void handle_map(struct wl_listener *listener, void *data) { | 276 | static 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 | ||
299 | static void handle_destroy(struct wl_listener *listener, void *data) { | 300 | static 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; | |||
34 | void sway_terminate(int exit_code) { | 34 | void 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) { | |||
112 | void server_fini(struct sway_server *server) { | 115 | void 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 | ||
117 | void server_run(struct sway_server *server) { | 121 | void 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 | */ | ||
153 | static 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 | |||
147 | static void arrange_children_of(struct sway_container *parent, | 160 | static 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 | ||
163 | static void arrange_children_of(struct sway_container *parent, | 177 | static 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 | ||
296 | void arrange_and_commit(struct sway_container *container) { | 319 | void 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 | ||
136 | static void _container_destroy(struct sway_container *cont) { | 136 | static 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; | 144 | void 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 | ||
292 | static 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 | ||
296 | bool container_reap_empty(struct sway_container *con) { | 296 | bool 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 | */ | ||
352 | struct sway_container *container_destroy(struct sway_container *con) { | 358 | struct 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 | ||
35 | struct sway_container *output_create( | 34 | struct 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 | ||
32 | void view_destroy(struct sway_view *view) { | 31 | void 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 | */ | ||
71 | void 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 | |||
69 | const char *view_get_title(struct sway_view *view) { | 82 | const 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 | ||
357 | void view_for_each_surface(struct sway_view *view, | 370 | void 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 | ||
526 | void view_unmap(struct sway_view *view) { | 542 | struct 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 | ||
556 | void view_update_position(struct sway_view *view, double lx, double ly) { | 556 | void 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 | ||
926 | bool view_is_visible(struct sway_view *view) { | 926 | bool 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 | ||
432 | bool workspace_is_visible(struct sway_container *ws) { | 432 | bool 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); |