aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop/transaction.c')
-rw-r--r--sway/desktop/transaction.c734
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
40static struct sway_transaction *transaction_create(void) { 42static struct sway_transaction *transaction_create(void) {
@@ -86,7 +88,11 @@ static void transaction_destroy(struct sway_transaction *transaction) {
86static void copy_output_state(struct sway_output *output, 88static 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
163static void transaction_add_node(struct sway_transaction *transaction, 168static 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
191static void apply_output_state(struct sway_output *output, 215static 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
199static void apply_workspace_state(struct sway_workspace *ws, 221static 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
208static void apply_container_state(struct sway_container *container, 228static 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
256static 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
272static 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
286static void arrange_container(struct sway_container *con,
287 int width, int height, bool title_bar, int gaps);
288
289static 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
382static 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
460static 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 478static 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 + 497static 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
532static 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
539static 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
558static 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
609static 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
627static 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
320static void transaction_commit(struct sway_transaction *transaction); 726static void transaction_commit_pending(void);
321
322// Return true if both transactions operate on the same nodes
323static 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
338static void transaction_progress_queue(void) { 728static 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
386static int handle_timeout(void *data) { 749static 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
848static 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
486static void set_instruction_ready( 859static 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
513void transaction_notify_view_ready_by_serial(struct sway_view *view, 887bool 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
522void transaction_notify_view_ready_by_geometry(struct sway_view *view, 898bool 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
535void transaction_notify_view_ready_immediately(struct sway_view *view) { 913static 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
543void 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. 935void 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. 939void transaction_commit_dirty_client(void) {
565 transaction_progress_queue(); 940 _transaction_commit_dirty(false);
566 }
567} 941}