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