diff options
Diffstat (limited to 'sway/desktop/transaction.c')
-rw-r--r-- | sway/desktop/transaction.c | 253 |
1 files changed, 119 insertions, 134 deletions
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index eac38991..b1f3fb32 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -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,9 +210,6 @@ 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, |
@@ -214,8 +238,8 @@ static void apply_container_state(struct sway_container *container, | |||
214 | struct sway_saved_buffer *saved_buf; | 238 | struct sway_saved_buffer *saved_buf; |
215 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | 239 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { |
216 | struct wlr_box box = { | 240 | struct wlr_box box = { |
217 | .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, | 241 | .x = saved_buf->x - view->saved_geometry.x, |
218 | .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, | 242 | .y = saved_buf->y - view->saved_geometry.y, |
219 | .width = saved_buf->width, | 243 | .width = saved_buf->width, |
220 | .height = saved_buf->height, | 244 | .height = saved_buf->height, |
221 | }; | 245 | }; |
@@ -238,6 +262,13 @@ static void apply_container_state(struct sway_container *container, | |||
238 | } | 262 | } |
239 | } | 263 | } |
240 | 264 | ||
265 | // If the view hasn't responded to the configure, center it within | ||
266 | // the container. This is important for fullscreen views which | ||
267 | // refuse to resize to the size of the output. | ||
268 | if (view && view->surface) { | ||
269 | view_center_surface(view); | ||
270 | } | ||
271 | |||
241 | // Damage the new location | 272 | // Damage the new location |
242 | desktop_damage_whole_container(container); | 273 | desktop_damage_whole_container(container); |
243 | if (view && view->surface) { | 274 | if (view && view->surface) { |
@@ -251,24 +282,6 @@ static void apply_container_state(struct sway_container *container, | |||
251 | desktop_damage_box(&box); | 282 | desktop_damage_box(&box); |
252 | } | 283 | } |
253 | 284 | ||
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 { | ||
262 | container->surface_x = container->current.content_x; | ||
263 | } | ||
264 | if (view->geometry.height < container->current.content_height) { | ||
265 | container->surface_y = container->current.content_y + | ||
266 | (container->current.content_height - view->geometry.height) / 2; | ||
267 | } else { | ||
268 | container->surface_y = container->current.content_y; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | if (!container->node.destroying) { | 285 | if (!container->node.destroying) { |
273 | container_discover_outputs(container); | 286 | container_discover_outputs(container); |
274 | } | 287 | } |
@@ -317,70 +330,25 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
317 | cursor_rebase_all(); | 330 | cursor_rebase_all(); |
318 | } | 331 | } |
319 | 332 | ||
320 | static void transaction_commit(struct sway_transaction *transaction); | 333 | static void transaction_commit_pending(void); |
321 | 334 | ||
322 | // Return true if both transactions operate on the same nodes | 335 | static void transaction_progress(void) { |
323 | static bool transaction_same_nodes(struct sway_transaction *a, | 336 | 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 | |||
338 | static void transaction_progress_queue(void) { | ||
339 | if (!server.transactions->length) { | ||
340 | return; | 337 | return; |
341 | } | 338 | } |
342 | // Only the first transaction in the queue is committed, so that's the one | 339 | 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; | 340 | return; |
347 | } | 341 | } |
348 | transaction_apply(transaction); | 342 | transaction_apply(server.queued_transaction); |
349 | transaction_destroy(transaction); | 343 | transaction_destroy(server.queued_transaction); |
350 | list_del(server.transactions, 0); | 344 | server.queued_transaction = NULL; |
351 | 345 | ||
352 | if (server.transactions->length == 0) { | 346 | if (!server.pending_transaction) { |
353 | // The transaction queue is empty, so we're done. | ||
354 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 347 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); |
355 | return; | 348 | return; |
356 | } | 349 | } |
357 | 350 | ||
358 | // If there's a bunch of consecutive transactions which all apply to the | 351 | 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 | } | 352 | } |
385 | 353 | ||
386 | static int handle_timeout(void *data) { | 354 | static int handle_timeout(void *data) { |
@@ -388,7 +356,7 @@ static int handle_timeout(void *data) { | |||
388 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", | 356 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", |
389 | transaction, transaction->num_waiting); | 357 | transaction, transaction->num_waiting); |
390 | transaction->num_waiting = 0; | 358 | transaction->num_waiting = 0; |
391 | transaction_progress_queue(); | 359 | transaction_progress(); |
392 | return 0; | 360 | return 0; |
393 | } | 361 | } |
394 | 362 | ||
@@ -400,6 +368,9 @@ static bool should_configure(struct sway_node *node, | |||
400 | if (node->destroying) { | 368 | if (node->destroying) { |
401 | return false; | 369 | return false; |
402 | } | 370 | } |
371 | if (!instruction->server_request) { | ||
372 | return false; | ||
373 | } | ||
403 | struct sway_container_state *cstate = &node->sway_container->current; | 374 | struct sway_container_state *cstate = &node->sway_container->current; |
404 | struct sway_container_state *istate = &instruction->container_state; | 375 | struct sway_container_state *istate = &instruction->container_state; |
405 | #if HAVE_XWAYLAND | 376 | #if HAVE_XWAYLAND |
@@ -431,13 +402,18 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
431 | struct sway_transaction_instruction *instruction = | 402 | struct sway_transaction_instruction *instruction = |
432 | transaction->instructions->items[i]; | 403 | transaction->instructions->items[i]; |
433 | struct sway_node *node = instruction->node; | 404 | struct sway_node *node = instruction->node; |
405 | bool hidden = node_is_view(node) && | ||
406 | !view_is_visible(node->sway_container->view); | ||
434 | if (should_configure(node, instruction)) { | 407 | if (should_configure(node, instruction)) { |
435 | instruction->serial = view_configure(node->sway_container->view, | 408 | instruction->serial = view_configure(node->sway_container->view, |
436 | instruction->container_state.content_x, | 409 | instruction->container_state.content_x, |
437 | instruction->container_state.content_y, | 410 | instruction->container_state.content_y, |
438 | instruction->container_state.content_width, | 411 | instruction->container_state.content_width, |
439 | instruction->container_state.content_height); | 412 | instruction->container_state.content_height); |
440 | ++transaction->num_waiting; | 413 | if (!hidden) { |
414 | instruction->waiting = true; | ||
415 | ++transaction->num_waiting; | ||
416 | } | ||
441 | 417 | ||
442 | // From here on we are rendering a saved buffer of the view, which | 418 | // From here on we are rendering a saved buffer of the view, which |
443 | // means we can send a frame done event to make the client redraw it | 419 | // means we can send a frame done event to make the client redraw it |
@@ -448,7 +424,8 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
448 | wlr_surface_send_frame_done( | 424 | wlr_surface_send_frame_done( |
449 | node->sway_container->view->surface, &now); | 425 | node->sway_container->view->surface, &now); |
450 | } | 426 | } |
451 | if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { | 427 | if (!hidden && node_is_view(node) && |
428 | wl_list_empty(&node->sway_container->view->saved_buffers)) { | ||
452 | view_save_buffer(node->sway_container->view); | 429 | view_save_buffer(node->sway_container->view); |
453 | memcpy(&node->sway_container->view->saved_geometry, | 430 | memcpy(&node->sway_container->view->saved_geometry, |
454 | &node->sway_container->view->geometry, | 431 | &node->sway_container->view->geometry, |
@@ -483,6 +460,17 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
483 | } | 460 | } |
484 | } | 461 | } |
485 | 462 | ||
463 | static void transaction_commit_pending(void) { | ||
464 | if (server.queued_transaction) { | ||
465 | return; | ||
466 | } | ||
467 | struct sway_transaction *transaction = server.pending_transaction; | ||
468 | server.pending_transaction = NULL; | ||
469 | server.queued_transaction = transaction; | ||
470 | transaction_commit(transaction); | ||
471 | transaction_progress(); | ||
472 | } | ||
473 | |||
486 | static void set_instruction_ready( | 474 | static void set_instruction_ready( |
487 | struct sway_transaction_instruction *instruction) { | 475 | struct sway_transaction_instruction *instruction) { |
488 | struct sway_transaction *transaction = instruction->transaction; | 476 | struct sway_transaction *transaction = instruction->transaction; |
@@ -501,13 +489,14 @@ static void set_instruction_ready( | |||
501 | } | 489 | } |
502 | 490 | ||
503 | // If the transaction has timed out then its num_waiting will be 0 already. | 491 | // If the transaction has timed out then its num_waiting will be 0 already. |
504 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 492 | if (instruction->waiting && transaction->num_waiting > 0 && |
493 | --transaction->num_waiting == 0) { | ||
505 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); | 494 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); |
506 | wl_event_source_timer_update(transaction->timer, 0); | 495 | wl_event_source_timer_update(transaction->timer, 0); |
507 | } | 496 | } |
508 | 497 | ||
509 | instruction->node->instruction = NULL; | 498 | instruction->node->instruction = NULL; |
510 | transaction_progress_queue(); | 499 | transaction_progress(); |
511 | } | 500 | } |
512 | 501 | ||
513 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 502 | void transaction_notify_view_ready_by_serial(struct sway_view *view, |
@@ -532,36 +521,32 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
532 | } | 521 | } |
533 | } | 522 | } |
534 | 523 | ||
535 | void transaction_notify_view_ready_immediately(struct sway_view *view) { | 524 | 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) { | 525 | if (!server.dirty_nodes->length) { |
545 | return; | 526 | return; |
546 | } | 527 | } |
547 | struct sway_transaction *transaction = transaction_create(); | 528 | |
548 | if (!transaction) { | 529 | if (!server.pending_transaction) { |
549 | return; | 530 | server.pending_transaction = transaction_create(); |
531 | if (!server.pending_transaction) { | ||
532 | return; | ||
533 | } | ||
550 | } | 534 | } |
535 | |||
551 | for (int i = 0; i < server.dirty_nodes->length; ++i) { | 536 | for (int i = 0; i < server.dirty_nodes->length; ++i) { |
552 | struct sway_node *node = server.dirty_nodes->items[i]; | 537 | struct sway_node *node = server.dirty_nodes->items[i]; |
553 | transaction_add_node(transaction, node); | 538 | transaction_add_node(server.pending_transaction, node, server_request); |
554 | node->dirty = false; | 539 | node->dirty = false; |
555 | } | 540 | } |
556 | server.dirty_nodes->length = 0; | 541 | server.dirty_nodes->length = 0; |
557 | 542 | ||
558 | list_add(server.transactions, transaction); | 543 | transaction_commit_pending(); |
544 | } | ||
559 | 545 | ||
560 | // We only commit the first transaction added to the queue. | 546 | void transaction_commit_dirty(void) { |
561 | if (server.transactions->length == 1) { | 547 | _transaction_commit_dirty(true); |
562 | transaction_commit(transaction); | 548 | } |
563 | // Attempting to progress the queue here is useful | 549 | |
564 | // if the transaction has nothing to wait for. | 550 | void transaction_commit_dirty_client(void) { |
565 | transaction_progress_queue(); | 551 | _transaction_commit_dirty(false); |
566 | } | ||
567 | } | 552 | } |