diff options
Diffstat (limited to 'sway/desktop/transaction.c')
-rw-r--r-- | sway/desktop/transaction.c | 734 |
1 files changed, 554 insertions, 180 deletions
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index eac38991..acc3e3f9 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -5,7 +5,7 @@ | |||
5 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/desktop.h" | 8 | #include "sway/scene_descriptor.h" |
9 | #include "sway/desktop/idle_inhibit_v1.h" | 9 | #include "sway/desktop/idle_inhibit_v1.h" |
10 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
@@ -35,6 +35,8 @@ struct sway_transaction_instruction { | |||
35 | struct sway_container_state container_state; | 35 | struct sway_container_state container_state; |
36 | }; | 36 | }; |
37 | uint32_t serial; | 37 | uint32_t serial; |
38 | bool server_request; | ||
39 | bool waiting; | ||
38 | }; | 40 | }; |
39 | 41 | ||
40 | static struct sway_transaction *transaction_create(void) { | 42 | static struct sway_transaction *transaction_create(void) { |
@@ -86,7 +88,11 @@ static void transaction_destroy(struct sway_transaction *transaction) { | |||
86 | static void copy_output_state(struct sway_output *output, | 88 | static void copy_output_state(struct sway_output *output, |
87 | struct sway_transaction_instruction *instruction) { | 89 | struct sway_transaction_instruction *instruction) { |
88 | struct sway_output_state *state = &instruction->output_state; | 90 | struct sway_output_state *state = &instruction->output_state; |
89 | state->workspaces = create_list(); | 91 | if (state->workspaces) { |
92 | state->workspaces->length = 0; | ||
93 | } else { | ||
94 | state->workspaces = create_list(); | ||
95 | } | ||
90 | list_cat(state->workspaces, output->workspaces); | 96 | list_cat(state->workspaces, output->workspaces); |
91 | 97 | ||
92 | state->active_workspace = output_get_active_workspace(output); | 98 | state->active_workspace = output_get_active_workspace(output); |
@@ -104,8 +110,16 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
104 | state->layout = ws->layout; | 110 | state->layout = ws->layout; |
105 | 111 | ||
106 | state->output = ws->output; | 112 | state->output = ws->output; |
107 | state->floating = create_list(); | 113 | if (state->floating) { |
108 | state->tiling = create_list(); | 114 | state->floating->length = 0; |
115 | } else { | ||
116 | state->floating = create_list(); | ||
117 | } | ||
118 | if (state->tiling) { | ||
119 | state->tiling->length = 0; | ||
120 | } else { | ||
121 | state->tiling = create_list(); | ||
122 | } | ||
109 | list_cat(state->floating, ws->floating); | 123 | list_cat(state->floating, ws->floating); |
110 | list_cat(state->tiling, ws->tiling); | 124 | list_cat(state->tiling, ws->tiling); |
111 | 125 | ||
@@ -115,8 +129,8 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
115 | // Set focused_inactive_child to the direct tiling child | 129 | // Set focused_inactive_child to the direct tiling child |
116 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); | 130 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); |
117 | if (focus) { | 131 | if (focus) { |
118 | while (focus->parent) { | 132 | while (focus->pending.parent) { |
119 | focus = focus->parent; | 133 | focus = focus->pending.parent; |
120 | } | 134 | } |
121 | } | 135 | } |
122 | state->focused_inactive_child = focus; | 136 | state->focused_inactive_child = focus; |
@@ -126,28 +140,19 @@ static void copy_container_state(struct sway_container *container, | |||
126 | struct sway_transaction_instruction *instruction) { | 140 | struct sway_transaction_instruction *instruction) { |
127 | struct sway_container_state *state = &instruction->container_state; | 141 | struct sway_container_state *state = &instruction->container_state; |
128 | 142 | ||
129 | state->layout = container->layout; | 143 | if (state->children) { |
130 | state->x = container->x; | 144 | list_free(state->children); |
131 | state->y = container->y; | 145 | } |
132 | state->width = container->width; | 146 | |
133 | state->height = container->height; | 147 | memcpy(state, &container->pending, sizeof(struct sway_container_state)); |
134 | state->fullscreen_mode = container->fullscreen_mode; | ||
135 | state->parent = container->parent; | ||
136 | state->workspace = container->workspace; | ||
137 | state->border = container->border; | ||
138 | state->border_thickness = container->border_thickness; | ||
139 | state->border_top = container->border_top; | ||
140 | state->border_left = container->border_left; | ||
141 | state->border_right = container->border_right; | ||
142 | state->border_bottom = container->border_bottom; | ||
143 | state->content_x = container->content_x; | ||
144 | state->content_y = container->content_y; | ||
145 | state->content_width = container->content_width; | ||
146 | state->content_height = container->content_height; | ||
147 | 148 | ||
148 | if (!container->view) { | 149 | if (!container->view) { |
150 | // We store a copy of the child list to avoid having it mutated after | ||
151 | // we copy the state. | ||
149 | state->children = create_list(); | 152 | state->children = create_list(); |
150 | list_cat(state->children, container->children); | 153 | list_cat(state->children, container->pending.children); |
154 | } else { | ||
155 | state->children = NULL; | ||
151 | } | 156 | } |
152 | 157 | ||
153 | struct sway_seat *seat = input_manager_current_seat(); | 158 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -161,14 +166,36 @@ static void copy_container_state(struct sway_container *container, | |||
161 | } | 166 | } |
162 | 167 | ||
163 | static void transaction_add_node(struct sway_transaction *transaction, | 168 | static void transaction_add_node(struct sway_transaction *transaction, |
164 | struct sway_node *node) { | 169 | struct sway_node *node, bool server_request) { |
165 | struct sway_transaction_instruction *instruction = | 170 | struct sway_transaction_instruction *instruction = NULL; |
166 | calloc(1, sizeof(struct sway_transaction_instruction)); | 171 | |
167 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | 172 | // Check if we have an instruction for this node already, in which case we |
168 | return; | 173 | // update that instead of creating a new one. |
174 | if (node->ntxnrefs > 0) { | ||
175 | for (int idx = 0; idx < transaction->instructions->length; idx++) { | ||
176 | struct sway_transaction_instruction *other = | ||
177 | transaction->instructions->items[idx]; | ||
178 | if (other->node == node) { | ||
179 | instruction = other; | ||
180 | break; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | if (!instruction) { | ||
186 | instruction = calloc(1, sizeof(struct sway_transaction_instruction)); | ||
187 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | ||
188 | return; | ||
189 | } | ||
190 | instruction->transaction = transaction; | ||
191 | instruction->node = node; | ||
192 | instruction->server_request = server_request; | ||
193 | |||
194 | list_add(transaction->instructions, instruction); | ||
195 | node->ntxnrefs++; | ||
196 | } else if (server_request) { | ||
197 | instruction->server_request = true; | ||
169 | } | 198 | } |
170 | instruction->transaction = transaction; | ||
171 | instruction->node = node; | ||
172 | 199 | ||
173 | switch (node->type) { | 200 | switch (node->type) { |
174 | case N_ROOT: | 201 | case N_ROOT: |
@@ -183,46 +210,24 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
183 | copy_container_state(node->sway_container, instruction); | 210 | copy_container_state(node->sway_container, instruction); |
184 | break; | 211 | break; |
185 | } | 212 | } |
186 | |||
187 | list_add(transaction->instructions, instruction); | ||
188 | node->ntxnrefs++; | ||
189 | } | 213 | } |
190 | 214 | ||
191 | static void apply_output_state(struct sway_output *output, | 215 | static void apply_output_state(struct sway_output *output, |
192 | struct sway_output_state *state) { | 216 | struct sway_output_state *state) { |
193 | output_damage_whole(output); | ||
194 | list_free(output->current.workspaces); | 217 | list_free(output->current.workspaces); |
195 | memcpy(&output->current, state, sizeof(struct sway_output_state)); | 218 | memcpy(&output->current, state, sizeof(struct sway_output_state)); |
196 | output_damage_whole(output); | ||
197 | } | 219 | } |
198 | 220 | ||
199 | static void apply_workspace_state(struct sway_workspace *ws, | 221 | static void apply_workspace_state(struct sway_workspace *ws, |
200 | struct sway_workspace_state *state) { | 222 | struct sway_workspace_state *state) { |
201 | output_damage_whole(ws->current.output); | ||
202 | list_free(ws->current.floating); | 223 | list_free(ws->current.floating); |
203 | list_free(ws->current.tiling); | 224 | list_free(ws->current.tiling); |
204 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); | 225 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); |
205 | output_damage_whole(ws->current.output); | ||
206 | } | 226 | } |
207 | 227 | ||
208 | static void apply_container_state(struct sway_container *container, | 228 | static void apply_container_state(struct sway_container *container, |
209 | struct sway_container_state *state) { | 229 | struct sway_container_state *state) { |
210 | struct sway_view *view = container->view; | 230 | struct sway_view *view = container->view; |
211 | // Damage the old location | ||
212 | desktop_damage_whole_container(container); | ||
213 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
214 | struct sway_saved_buffer *saved_buf; | ||
215 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
216 | struct wlr_box box = { | ||
217 | .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, | ||
218 | .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, | ||
219 | .width = saved_buf->width, | ||
220 | .height = saved_buf->height, | ||
221 | }; | ||
222 | desktop_damage_box(&box); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | // There are separate children lists for each instruction state, the | 231 | // There are separate children lists for each instruction state, the |
227 | // container's current state and the container's pending state | 232 | // container's current state and the container's pending state |
228 | // (ie. con->children). The list itself needs to be freed here. | 233 | // (ie. con->children). The list itself needs to be freed here. |
@@ -232,46 +237,449 @@ static void apply_container_state(struct sway_container *container, | |||
232 | 237 | ||
233 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 238 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
234 | 239 | ||
235 | if (view && !wl_list_empty(&view->saved_buffers)) { | 240 | if (view) { |
236 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 241 | if (view->saved_surface_tree) { |
237 | view_remove_saved_buffer(view); | 242 | if (!container->node.destroying || container->node.ntxnrefs == 1) { |
243 | view_remove_saved_buffer(view); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | // If the view hasn't responded to the configure, center it within | ||
248 | // the container. This is important for fullscreen views which | ||
249 | // refuse to resize to the size of the output. | ||
250 | if (view->surface) { | ||
251 | view_center_and_clip_surface(view); | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | static void arrange_title_bar(struct sway_container *con, | ||
257 | int x, int y, int width, int height) { | ||
258 | container_update(con); | ||
259 | |||
260 | bool has_title_bar = height > 0; | ||
261 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); | ||
262 | if (!has_title_bar) { | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); | ||
267 | |||
268 | con->title_width = width; | ||
269 | container_arrange_title_bar(con); | ||
270 | } | ||
271 | |||
272 | static void disable_container(struct sway_container *con) { | ||
273 | if (con->view) { | ||
274 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
275 | } else { | ||
276 | for (int i = 0; i < con->current.children->length; i++) { | ||
277 | struct sway_container *child = con->current.children->items[i]; | ||
278 | |||
279 | wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); | ||
280 | |||
281 | disable_container(child); | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static void arrange_container(struct sway_container *con, | ||
287 | int width, int height, bool title_bar, int gaps); | ||
288 | |||
289 | static void arrange_children(enum sway_container_layout layout, list_t *children, | ||
290 | struct sway_container *active, struct wlr_scene_tree *content, | ||
291 | int width, int height, int gaps) { | ||
292 | int title_bar_height = container_titlebar_height(); | ||
293 | |||
294 | if (layout == L_TABBED) { | ||
295 | struct sway_container *first = children->length == 1 ? | ||
296 | ((struct sway_container *)children->items[0]) : NULL; | ||
297 | if (config->hide_lone_tab && first && first->view && | ||
298 | first->current.border != B_NORMAL) { | ||
299 | title_bar_height = 0; | ||
300 | } | ||
301 | |||
302 | double w = (double) width / children->length; | ||
303 | int title_offset = 0; | ||
304 | for (int i = 0; i < children->length; i++) { | ||
305 | struct sway_container *child = children->items[i]; | ||
306 | bool activated = child == active; | ||
307 | int next_title_offset = round(w * i + w); | ||
308 | |||
309 | arrange_title_bar(child, title_offset, -title_bar_height, | ||
310 | next_title_offset - title_offset, title_bar_height); | ||
311 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
312 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); | ||
313 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
314 | |||
315 | if (activated) { | ||
316 | arrange_container(child, width, height - title_bar_height, | ||
317 | false, 0); | ||
318 | } else { | ||
319 | disable_container(child); | ||
320 | } | ||
321 | |||
322 | title_offset = next_title_offset; | ||
238 | } | 323 | } |
324 | } else if (layout == L_STACKED) { | ||
325 | struct sway_container *first = children->length == 1 ? | ||
326 | ((struct sway_container *)children->items[0]) : NULL; | ||
327 | if (config->hide_lone_tab && first && first->view && | ||
328 | first->current.border != B_NORMAL) { | ||
329 | title_bar_height = 0; | ||
330 | } | ||
331 | |||
332 | int title_height = title_bar_height * children->length; | ||
333 | |||
334 | int y = 0; | ||
335 | for (int i = 0; i < children->length; i++) { | ||
336 | struct sway_container *child = children->items[i]; | ||
337 | bool activated = child == active; | ||
338 | |||
339 | arrange_title_bar(child, 0, y - title_height, width, title_bar_height); | ||
340 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
341 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); | ||
342 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
343 | |||
344 | if (activated) { | ||
345 | arrange_container(child, width, height - title_height, | ||
346 | false, 0); | ||
347 | } else { | ||
348 | disable_container(child); | ||
349 | } | ||
350 | |||
351 | y += title_bar_height; | ||
352 | } | ||
353 | } else if (layout == L_VERT) { | ||
354 | int off = 0; | ||
355 | for (int i = 0; i < children->length; i++) { | ||
356 | struct sway_container *child = children->items[i]; | ||
357 | int cheight = child->current.height; | ||
358 | |||
359 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
360 | wlr_scene_node_set_position(&child->scene_tree->node, 0, off); | ||
361 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
362 | arrange_container(child, width, cheight, true, gaps); | ||
363 | off += cheight + gaps; | ||
364 | } | ||
365 | } else if (layout == L_HORIZ) { | ||
366 | int off = 0; | ||
367 | for (int i = 0; i < children->length; i++) { | ||
368 | struct sway_container *child = children->items[i]; | ||
369 | int cwidth = child->current.width; | ||
370 | |||
371 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
372 | wlr_scene_node_set_position(&child->scene_tree->node, off, 0); | ||
373 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
374 | arrange_container(child, cwidth, height, true, gaps); | ||
375 | off += cwidth + gaps; | ||
376 | } | ||
377 | } else { | ||
378 | sway_assert(false, "unreachable"); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | static void arrange_container(struct sway_container *con, | ||
383 | int width, int height, bool title_bar, int gaps) { | ||
384 | // this container might have previously been in the scratchpad, | ||
385 | // make sure it's enabled for viewing | ||
386 | wlr_scene_node_set_enabled(&con->scene_tree->node, true); | ||
387 | |||
388 | if (con->output_handler) { | ||
389 | wlr_scene_buffer_set_dest_size(con->output_handler, width, height); | ||
390 | } | ||
391 | |||
392 | if (con->view) { | ||
393 | int border_top = container_titlebar_height(); | ||
394 | int border_width = con->current.border_thickness; | ||
395 | |||
396 | if (title_bar && con->current.border != B_NORMAL) { | ||
397 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
398 | wlr_scene_node_set_enabled(&con->border.top->node, true); | ||
399 | } else { | ||
400 | wlr_scene_node_set_enabled(&con->border.top->node, false); | ||
401 | } | ||
402 | |||
403 | if (con->current.border == B_NORMAL) { | ||
404 | if (title_bar) { | ||
405 | arrange_title_bar(con, 0, 0, width, border_top); | ||
406 | } else { | ||
407 | border_top = 0; | ||
408 | // should be handled by the parent container | ||
409 | } | ||
410 | } else if (con->current.border == B_PIXEL) { | ||
411 | container_update(con); | ||
412 | border_top = title_bar && con->current.border_top ? border_width : 0; | ||
413 | } else if (con->current.border == B_NONE) { | ||
414 | container_update(con); | ||
415 | border_top = 0; | ||
416 | border_width = 0; | ||
417 | } else if (con->current.border == B_CSD) { | ||
418 | border_top = 0; | ||
419 | border_width = 0; | ||
420 | } else { | ||
421 | sway_assert(false, "unreachable"); | ||
422 | } | ||
423 | |||
424 | int border_bottom = con->current.border_bottom ? border_width : 0; | ||
425 | int border_left = con->current.border_left ? border_width : 0; | ||
426 | int border_right = con->current.border_right ? border_width : 0; | ||
427 | |||
428 | wlr_scene_rect_set_size(con->border.top, width, border_top); | ||
429 | wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); | ||
430 | wlr_scene_rect_set_size(con->border.left, | ||
431 | border_left, height - border_top - border_bottom); | ||
432 | wlr_scene_rect_set_size(con->border.right, | ||
433 | border_right, height - border_top - border_bottom); | ||
434 | |||
435 | wlr_scene_node_set_position(&con->border.top->node, 0, 0); | ||
436 | wlr_scene_node_set_position(&con->border.bottom->node, | ||
437 | 0, height - border_bottom); | ||
438 | wlr_scene_node_set_position(&con->border.left->node, | ||
439 | 0, border_top); | ||
440 | wlr_scene_node_set_position(&con->border.right->node, | ||
441 | width - border_right, border_top); | ||
442 | |||
443 | // make sure to reparent, it's possible that the client just came out of | ||
444 | // fullscreen mode where the parent of the surface is not the container | ||
445 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
446 | wlr_scene_node_set_position(&con->view->scene_tree->node, | ||
447 | border_left, border_top); | ||
448 | } else { | ||
449 | // make sure to disable the title bar if the parent is not managing it | ||
450 | if (title_bar) { | ||
451 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
452 | } | ||
453 | |||
454 | arrange_children(con->current.layout, con->current.children, | ||
455 | con->current.focused_inactive_child, con->content_tree, | ||
456 | width, height, gaps); | ||
239 | } | 457 | } |
458 | } | ||
459 | |||
460 | static int container_get_gaps(struct sway_container *con) { | ||
461 | struct sway_workspace *ws = con->current.workspace; | ||
462 | struct sway_container *temp = con; | ||
463 | while (temp) { | ||
464 | enum sway_container_layout layout; | ||
465 | if (temp->current.parent) { | ||
466 | layout = temp->current.parent->current.layout; | ||
467 | } else { | ||
468 | layout = ws->current.layout; | ||
469 | } | ||
470 | if (layout == L_TABBED || layout == L_STACKED) { | ||
471 | return 0; | ||
472 | } | ||
473 | temp = temp->pending.parent; | ||
474 | } | ||
475 | return ws->gaps_inner; | ||
476 | } | ||
240 | 477 | ||
241 | // Damage the new location | 478 | static void arrange_fullscreen(struct wlr_scene_tree *tree, |
242 | desktop_damage_whole_container(container); | 479 | struct sway_container *fs, struct sway_workspace *ws, |
243 | if (view && view->surface) { | 480 | int width, int height) { |
244 | struct wlr_surface *surface = view->surface; | 481 | struct wlr_scene_node *fs_node; |
245 | struct wlr_box box = { | 482 | if (fs->view) { |
246 | .x = container->current.content_x - view->geometry.x, | 483 | fs_node = &fs->view->scene_tree->node; |
247 | .y = container->current.content_y - view->geometry.y, | 484 | |
248 | .width = surface->current.width, | 485 | // if we only care about the view, disable any decorations |
249 | .height = surface->current.height, | 486 | wlr_scene_node_set_enabled(&fs->scene_tree->node, false); |
250 | }; | 487 | } else { |
251 | desktop_damage_box(&box); | 488 | fs_node = &fs->scene_tree->node; |
489 | arrange_container(fs, width, height, true, container_get_gaps(fs)); | ||
252 | } | 490 | } |
253 | 491 | ||
254 | // If the view hasn't responded to the configure, center it within | 492 | wlr_scene_node_reparent(fs_node, tree); |
255 | // the container. This is important for fullscreen views which | 493 | wlr_scene_node_lower_to_bottom(fs_node); |
256 | // refuse to resize to the size of the output. | 494 | wlr_scene_node_set_position(fs_node, 0, 0); |
257 | if (view && view->surface) { | 495 | } |
258 | if (view->geometry.width < container->current.content_width) { | 496 | |
259 | container->surface_x = container->current.content_x + | 497 | static void arrange_workspace_floating(struct sway_workspace *ws) { |
260 | (container->current.content_width - view->geometry.width) / 2; | 498 | for (int i = 0; i < ws->current.floating->length; i++) { |
499 | struct sway_container *floater = ws->current.floating->items[i]; | ||
500 | struct wlr_scene_tree *layer = root->layers.floating; | ||
501 | |||
502 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
503 | continue; | ||
504 | } | ||
505 | |||
506 | if (root->fullscreen_global) { | ||
507 | if (container_is_transient_for(floater, root->fullscreen_global)) { | ||
508 | layer = root->layers.fullscreen_global; | ||
509 | } | ||
261 | } else { | 510 | } else { |
262 | container->surface_x = container->current.content_x; | 511 | for (int i = 0; i < root->outputs->length; i++) { |
512 | struct sway_output *output = root->outputs->items[i]; | ||
513 | struct sway_workspace *active = output->current.active_workspace; | ||
514 | |||
515 | if (active && active->fullscreen && | ||
516 | container_is_transient_for(floater, active->fullscreen)) { | ||
517 | layer = root->layers.fullscreen; | ||
518 | } | ||
519 | } | ||
520 | } | ||
521 | |||
522 | wlr_scene_node_reparent(&floater->scene_tree->node, layer); | ||
523 | wlr_scene_node_set_position(&floater->scene_tree->node, | ||
524 | floater->current.x, floater->current.y); | ||
525 | wlr_scene_node_set_enabled(&floater->scene_tree->node, true); | ||
526 | |||
527 | arrange_container(floater, floater->current.width, floater->current.height, | ||
528 | true, ws->gaps_inner); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | static void arrange_workspace_tiling(struct sway_workspace *ws, | ||
533 | int width, int height) { | ||
534 | arrange_children(ws->current.layout, ws->current.tiling, | ||
535 | ws->current.focused_inactive_child, ws->layers.tiling, | ||
536 | width, height, ws->gaps_inner); | ||
537 | } | ||
538 | |||
539 | static void disable_workspace(struct sway_workspace *ws) { | ||
540 | // if any containers were just moved to a disabled workspace it will | ||
541 | // have the parent of the old workspace. Move the workspace so that it won't | ||
542 | // be shown. | ||
543 | for (int i = 0; i < ws->current.tiling->length; i++) { | ||
544 | struct sway_container *child = ws->current.tiling->items[i]; | ||
545 | |||
546 | wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); | ||
547 | disable_container(child); | ||
548 | } | ||
549 | |||
550 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
551 | struct sway_container *floater = ws->current.floating->items[i]; | ||
552 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
553 | disable_container(floater); | ||
554 | wlr_scene_node_set_enabled(&floater->scene_tree->node, false); | ||
555 | } | ||
556 | } | ||
557 | |||
558 | static void arrange_output(struct sway_output *output, int width, int height) { | ||
559 | for (int i = 0; i < output->current.workspaces->length; i++) { | ||
560 | struct sway_workspace *child = output->current.workspaces->items[i]; | ||
561 | |||
562 | bool activated = output->current.active_workspace == child; | ||
563 | |||
564 | wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); | ||
565 | wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); | ||
566 | |||
567 | for (int i = 0; i < child->current.floating->length; i++) { | ||
568 | struct sway_container *floater = child->current.floating->items[i]; | ||
569 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
570 | wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); | ||
263 | } | 571 | } |
264 | if (view->geometry.height < container->current.content_height) { | 572 | |
265 | container->surface_y = container->current.content_y + | 573 | if (activated) { |
266 | (container->current.content_height - view->geometry.height) / 2; | 574 | struct sway_container *fs = child->current.fullscreen; |
575 | wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); | ||
576 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); | ||
577 | |||
578 | arrange_workspace_floating(child); | ||
579 | |||
580 | wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); | ||
581 | wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); | ||
582 | wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); | ||
583 | |||
584 | if (fs) { | ||
585 | wlr_scene_rect_set_size(output->fullscreen_background, width, height); | ||
586 | |||
587 | arrange_fullscreen(child->layers.fullscreen, fs, child, | ||
588 | width, height); | ||
589 | } else { | ||
590 | struct wlr_box *area = &output->usable_area; | ||
591 | struct side_gaps *gaps = &child->current_gaps; | ||
592 | |||
593 | wlr_scene_node_set_position(&child->layers.tiling->node, | ||
594 | gaps->left + area->x, gaps->top + area->y); | ||
595 | |||
596 | arrange_workspace_tiling(child, | ||
597 | area->width - gaps->left - gaps->right, | ||
598 | area->height - gaps->top - gaps->bottom); | ||
599 | } | ||
267 | } else { | 600 | } else { |
268 | container->surface_y = container->current.content_y; | 601 | wlr_scene_node_set_enabled(&child->layers.tiling->node, false); |
602 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); | ||
603 | |||
604 | disable_workspace(child); | ||
269 | } | 605 | } |
270 | } | 606 | } |
607 | } | ||
608 | |||
609 | static void arrange_popup(struct wlr_scene_tree *popup) { | ||
610 | struct wlr_scene_node *node; | ||
611 | wl_list_for_each(node, &popup->children, link) { | ||
612 | struct sway_xdg_popup *popup = scene_descriptor_try_get(node, | ||
613 | SWAY_SCENE_DESC_POPUP); | ||
614 | |||
615 | // the popup layer may have popups from layer_shell surfaces, in this | ||
616 | // case those don't have a scene descriptor, so lets skip those here. | ||
617 | if (popup) { | ||
618 | struct wlr_scene_tree *tree = popup->view->content_tree; | ||
619 | |||
620 | int lx, ly; | ||
621 | wlr_scene_node_coords(&tree->node, &lx, &ly); | ||
622 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
623 | } | ||
624 | } | ||
625 | } | ||
626 | |||
627 | static void arrange_root(struct sway_root *root) { | ||
628 | struct sway_container *fs = root->fullscreen_global; | ||
629 | |||
630 | wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); | ||
631 | wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); | ||
632 | wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); | ||
633 | wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); | ||
634 | wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); | ||
635 | wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); | ||
636 | |||
637 | // hide all contents in the scratchpad | ||
638 | for (int i = 0; i < root->scratchpad->length; i++) { | ||
639 | struct sway_container *con = root->scratchpad->items[i]; | ||
640 | |||
641 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); | ||
642 | } | ||
643 | |||
644 | if (fs) { | ||
645 | for (int i = 0; i < root->outputs->length; i++) { | ||
646 | struct sway_output *output = root->outputs->items[i]; | ||
647 | struct sway_workspace *ws = output->current.active_workspace; | ||
648 | |||
649 | if (ws) { | ||
650 | arrange_workspace_floating(ws); | ||
651 | } | ||
652 | } | ||
271 | 653 | ||
272 | if (!container->node.destroying) { | 654 | arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, |
273 | container_discover_outputs(container); | 655 | root->width, root->height); |
656 | } else { | ||
657 | for (int i = 0; i < root->outputs->length; i++) { | ||
658 | struct sway_output *output = root->outputs->items[i]; | ||
659 | |||
660 | wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); | ||
661 | |||
662 | wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); | ||
663 | wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); | ||
664 | wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); | ||
665 | wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); | ||
666 | wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); | ||
667 | wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); | ||
668 | wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); | ||
669 | |||
670 | wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); | ||
671 | wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); | ||
672 | wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); | ||
673 | wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); | ||
674 | wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); | ||
675 | wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); | ||
676 | wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); | ||
677 | |||
678 | arrange_output(output, output->width, output->height); | ||
679 | } | ||
274 | } | 680 | } |
681 | |||
682 | arrange_popup(root->layers.popup); | ||
275 | } | 683 | } |
276 | 684 | ||
277 | /** | 685 | /** |
@@ -313,74 +721,29 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
313 | 721 | ||
314 | node->instruction = NULL; | 722 | node->instruction = NULL; |
315 | } | 723 | } |
316 | |||
317 | cursor_rebase_all(); | ||
318 | } | 724 | } |
319 | 725 | ||
320 | static void transaction_commit(struct sway_transaction *transaction); | 726 | static void transaction_commit_pending(void); |
321 | |||
322 | // Return true if both transactions operate on the same nodes | ||
323 | static bool transaction_same_nodes(struct sway_transaction *a, | ||
324 | struct sway_transaction *b) { | ||
325 | if (a->instructions->length != b->instructions->length) { | ||
326 | return false; | ||
327 | } | ||
328 | for (int i = 0; i < a->instructions->length; ++i) { | ||
329 | struct sway_transaction_instruction *a_inst = a->instructions->items[i]; | ||
330 | struct sway_transaction_instruction *b_inst = b->instructions->items[i]; | ||
331 | if (a_inst->node != b_inst->node) { | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
335 | return true; | ||
336 | } | ||
337 | 727 | ||
338 | static void transaction_progress_queue(void) { | 728 | static void transaction_progress(void) { |
339 | if (!server.transactions->length) { | 729 | if (!server.queued_transaction) { |
340 | return; | 730 | return; |
341 | } | 731 | } |
342 | // Only the first transaction in the queue is committed, so that's the one | 732 | if (server.queued_transaction->num_waiting > 0) { |
343 | // we try to process. | ||
344 | struct sway_transaction *transaction = server.transactions->items[0]; | ||
345 | if (transaction->num_waiting) { | ||
346 | return; | 733 | return; |
347 | } | 734 | } |
348 | transaction_apply(transaction); | 735 | transaction_apply(server.queued_transaction); |
349 | transaction_destroy(transaction); | 736 | arrange_root(root); |
350 | list_del(server.transactions, 0); | 737 | cursor_rebase_all(); |
738 | transaction_destroy(server.queued_transaction); | ||
739 | server.queued_transaction = NULL; | ||
351 | 740 | ||
352 | if (server.transactions->length == 0) { | 741 | if (!server.pending_transaction) { |
353 | // The transaction queue is empty, so we're done. | 742 | sway_idle_inhibit_v1_check_active(); |
354 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | ||
355 | return; | 743 | return; |
356 | } | 744 | } |
357 | 745 | ||
358 | // If there's a bunch of consecutive transactions which all apply to the | 746 | transaction_commit_pending(); |
359 | // same views, skip all except the last one. | ||
360 | while (server.transactions->length >= 2) { | ||
361 | struct sway_transaction *txn = server.transactions->items[0]; | ||
362 | struct sway_transaction *dup = NULL; | ||
363 | |||
364 | for (int i = 1; i < server.transactions->length; i++) { | ||
365 | struct sway_transaction *maybe_dup = server.transactions->items[i]; | ||
366 | if (transaction_same_nodes(txn, maybe_dup)) { | ||
367 | dup = maybe_dup; | ||
368 | break; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (dup) { | ||
373 | list_del(server.transactions, 0); | ||
374 | transaction_destroy(txn); | ||
375 | } else { | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | // We again commit the first transaction in the queue to process it. | ||
381 | transaction = server.transactions->items[0]; | ||
382 | transaction_commit(transaction); | ||
383 | transaction_progress_queue(); | ||
384 | } | 747 | } |
385 | 748 | ||
386 | static int handle_timeout(void *data) { | 749 | static int handle_timeout(void *data) { |
@@ -388,7 +751,7 @@ static int handle_timeout(void *data) { | |||
388 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", | 751 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", |
389 | transaction, transaction->num_waiting); | 752 | transaction, transaction->num_waiting); |
390 | transaction->num_waiting = 0; | 753 | transaction->num_waiting = 0; |
391 | transaction_progress_queue(); | 754 | transaction_progress(); |
392 | return 0; | 755 | return 0; |
393 | } | 756 | } |
394 | 757 | ||
@@ -400,6 +763,9 @@ static bool should_configure(struct sway_node *node, | |||
400 | if (node->destroying) { | 763 | if (node->destroying) { |
401 | return false; | 764 | return false; |
402 | } | 765 | } |
766 | if (!instruction->server_request) { | ||
767 | return false; | ||
768 | } | ||
403 | struct sway_container_state *cstate = &node->sway_container->current; | 769 | struct sway_container_state *cstate = &node->sway_container->current; |
404 | struct sway_container_state *istate = &instruction->container_state; | 770 | struct sway_container_state *istate = &instruction->container_state; |
405 | #if HAVE_XWAYLAND | 771 | #if HAVE_XWAYLAND |
@@ -431,28 +797,24 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
431 | struct sway_transaction_instruction *instruction = | 797 | struct sway_transaction_instruction *instruction = |
432 | transaction->instructions->items[i]; | 798 | transaction->instructions->items[i]; |
433 | struct sway_node *node = instruction->node; | 799 | struct sway_node *node = instruction->node; |
800 | bool hidden = node_is_view(node) && !node->destroying && | ||
801 | !view_is_visible(node->sway_container->view); | ||
434 | if (should_configure(node, instruction)) { | 802 | if (should_configure(node, instruction)) { |
435 | instruction->serial = view_configure(node->sway_container->view, | 803 | instruction->serial = view_configure(node->sway_container->view, |
436 | instruction->container_state.content_x, | 804 | instruction->container_state.content_x, |
437 | instruction->container_state.content_y, | 805 | instruction->container_state.content_y, |
438 | instruction->container_state.content_width, | 806 | instruction->container_state.content_width, |
439 | instruction->container_state.content_height); | 807 | instruction->container_state.content_height); |
440 | ++transaction->num_waiting; | 808 | if (!hidden) { |
441 | 809 | instruction->waiting = true; | |
442 | // From here on we are rendering a saved buffer of the view, which | 810 | ++transaction->num_waiting; |
443 | // means we can send a frame done event to make the client redraw it | 811 | } |
444 | // as soon as possible. Additionally, this is required if a view is | 812 | |
445 | // mapping and its default geometry doesn't intersect an output. | 813 | view_send_frame_done(node->sway_container->view); |
446 | struct timespec now; | 814 | } |
447 | clock_gettime(CLOCK_MONOTONIC, &now); | 815 | if (!hidden && node_is_view(node) && |
448 | wlr_surface_send_frame_done( | 816 | !node->sway_container->view->saved_surface_tree) { |
449 | node->sway_container->view->surface, &now); | ||
450 | } | ||
451 | if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { | ||
452 | view_save_buffer(node->sway_container->view); | 817 | view_save_buffer(node->sway_container->view); |
453 | memcpy(&node->sway_container->view->saved_geometry, | ||
454 | &node->sway_container->view->geometry, | ||
455 | sizeof(struct wlr_box)); | ||
456 | } | 818 | } |
457 | node->instruction = instruction; | 819 | node->instruction = instruction; |
458 | } | 820 | } |
@@ -483,6 +845,17 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
483 | } | 845 | } |
484 | } | 846 | } |
485 | 847 | ||
848 | static void transaction_commit_pending(void) { | ||
849 | if (server.queued_transaction) { | ||
850 | return; | ||
851 | } | ||
852 | struct sway_transaction *transaction = server.pending_transaction; | ||
853 | server.pending_transaction = NULL; | ||
854 | server.queued_transaction = transaction; | ||
855 | transaction_commit(transaction); | ||
856 | transaction_progress(); | ||
857 | } | ||
858 | |||
486 | static void set_instruction_ready( | 859 | static void set_instruction_ready( |
487 | struct sway_transaction_instruction *instruction) { | 860 | struct sway_transaction_instruction *instruction) { |
488 | struct sway_transaction *transaction = instruction->transaction; | 861 | struct sway_transaction *transaction = instruction->transaction; |
@@ -501,25 +874,28 @@ static void set_instruction_ready( | |||
501 | } | 874 | } |
502 | 875 | ||
503 | // If the transaction has timed out then its num_waiting will be 0 already. | 876 | // If the transaction has timed out then its num_waiting will be 0 already. |
504 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 877 | if (instruction->waiting && transaction->num_waiting > 0 && |
878 | --transaction->num_waiting == 0) { | ||
505 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); | 879 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); |
506 | wl_event_source_timer_update(transaction->timer, 0); | 880 | wl_event_source_timer_update(transaction->timer, 0); |
507 | } | 881 | } |
508 | 882 | ||
509 | instruction->node->instruction = NULL; | 883 | instruction->node->instruction = NULL; |
510 | transaction_progress_queue(); | 884 | transaction_progress(); |
511 | } | 885 | } |
512 | 886 | ||
513 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 887 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
514 | uint32_t serial) { | 888 | uint32_t serial) { |
515 | struct sway_transaction_instruction *instruction = | 889 | struct sway_transaction_instruction *instruction = |
516 | view->container->node.instruction; | 890 | view->container->node.instruction; |
517 | if (instruction != NULL && instruction->serial == serial) { | 891 | if (instruction != NULL && instruction->serial == serial) { |
518 | set_instruction_ready(instruction); | 892 | set_instruction_ready(instruction); |
893 | return true; | ||
519 | } | 894 | } |
895 | return false; | ||
520 | } | 896 | } |
521 | 897 | ||
522 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 898 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
523 | double x, double y, int width, int height) { | 899 | double x, double y, int width, int height) { |
524 | struct sway_transaction_instruction *instruction = | 900 | struct sway_transaction_instruction *instruction = |
525 | view->container->node.instruction; | 901 | view->container->node.instruction; |
@@ -529,39 +905,37 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
529 | instruction->container_state.content_width == width && | 905 | instruction->container_state.content_width == width && |
530 | instruction->container_state.content_height == height) { | 906 | instruction->container_state.content_height == height) { |
531 | set_instruction_ready(instruction); | 907 | set_instruction_ready(instruction); |
908 | return true; | ||
532 | } | 909 | } |
910 | return false; | ||
533 | } | 911 | } |
534 | 912 | ||
535 | void transaction_notify_view_ready_immediately(struct sway_view *view) { | 913 | static void _transaction_commit_dirty(bool server_request) { |
536 | struct sway_transaction_instruction *instruction = | ||
537 | view->container->node.instruction; | ||
538 | if (instruction != NULL) { | ||
539 | set_instruction_ready(instruction); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | void transaction_commit_dirty(void) { | ||
544 | if (!server.dirty_nodes->length) { | 914 | if (!server.dirty_nodes->length) { |
545 | return; | 915 | return; |
546 | } | 916 | } |
547 | struct sway_transaction *transaction = transaction_create(); | 917 | |
548 | if (!transaction) { | 918 | if (!server.pending_transaction) { |
549 | return; | 919 | server.pending_transaction = transaction_create(); |
920 | if (!server.pending_transaction) { | ||
921 | return; | ||
922 | } | ||
550 | } | 923 | } |
924 | |||
551 | for (int i = 0; i < server.dirty_nodes->length; ++i) { | 925 | for (int i = 0; i < server.dirty_nodes->length; ++i) { |
552 | struct sway_node *node = server.dirty_nodes->items[i]; | 926 | struct sway_node *node = server.dirty_nodes->items[i]; |
553 | transaction_add_node(transaction, node); | 927 | transaction_add_node(server.pending_transaction, node, server_request); |
554 | node->dirty = false; | 928 | node->dirty = false; |
555 | } | 929 | } |
556 | server.dirty_nodes->length = 0; | 930 | server.dirty_nodes->length = 0; |
557 | 931 | ||
558 | list_add(server.transactions, transaction); | 932 | transaction_commit_pending(); |
933 | } | ||
559 | 934 | ||
560 | // We only commit the first transaction added to the queue. | 935 | void transaction_commit_dirty(void) { |
561 | if (server.transactions->length == 1) { | 936 | _transaction_commit_dirty(true); |
562 | transaction_commit(transaction); | 937 | } |
563 | // Attempting to progress the queue here is useful | 938 | |
564 | // if the transaction has nothing to wait for. | 939 | void transaction_commit_dirty_client(void) { |
565 | transaction_progress_queue(); | 940 | _transaction_commit_dirty(false); |
566 | } | ||
567 | } | 941 | } |