diff options
author | Alexander Orzechowski <alex@ozal.ski> | 2024-01-18 10:01:12 -0500 |
---|---|---|
committer | Kirill Primak <vyivel@eclair.cafe> | 2024-01-18 18:36:54 +0300 |
commit | 2e53de80bb0f4c93e74ae050fa07e78f18e909d9 (patch) | |
tree | 155e4ee92c60445d61e7e516e15d48fc1d68a3fa | |
parent | container: Don't track outputs (diff) | |
download | sway-2e53de80bb0f4c93e74ae050fa07e78f18e909d9.tar.gz sway-2e53de80bb0f4c93e74ae050fa07e78f18e909d9.tar.zst sway-2e53de80bb0f4c93e74ae050fa07e78f18e909d9.zip |
scene_graph: Arrange scene graph on transaction apply
-rw-r--r-- | sway/desktop/transaction.c | 430 |
1 files changed, 428 insertions, 2 deletions
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ba9d0648..0755c8a0 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -5,6 +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/scene_descriptor.h" | ||
8 | #include "sway/desktop/idle_inhibit_v1.h" | 9 | #include "sway/desktop/idle_inhibit_v1.h" |
9 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
10 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
@@ -252,6 +253,431 @@ static void apply_container_state(struct sway_container *container, | |||
252 | } | 253 | } |
253 | } | 254 | } |
254 | 255 | ||
256 | static void arrange_title_bar(struct sway_container *con, | ||
257 | int x, int y, int width, int height) { | ||
258 | container_update(con); | ||
259 | |||
260 | bool has_title_bar = height > 0; | ||
261 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); | ||
262 | if (!has_title_bar) { | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); | ||
267 | |||
268 | con->title_width = width; | ||
269 | container_arrange_title_bar(con); | ||
270 | } | ||
271 | |||
272 | static void disable_container(struct sway_container *con) { | ||
273 | if (con->view) { | ||
274 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
275 | } else { | ||
276 | for (int i = 0; i < con->current.children->length; i++) { | ||
277 | struct sway_container *child = con->current.children->items[i]; | ||
278 | |||
279 | wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); | ||
280 | |||
281 | disable_container(child); | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static void arrange_container(struct sway_container *con, | ||
287 | int width, int height, bool title_bar, int gaps); | ||
288 | |||
289 | static void arrange_children(enum sway_container_layout layout, list_t *children, | ||
290 | struct sway_container *active, struct wlr_scene_tree *content, | ||
291 | int width, int height, int gaps) { | ||
292 | int title_bar_height = container_titlebar_height(); | ||
293 | |||
294 | if (layout == L_TABBED) { | ||
295 | struct sway_container *first = children->length == 1 ? | ||
296 | ((struct sway_container *)children->items[0]) : NULL; | ||
297 | if (config->hide_lone_tab && first && first->view && | ||
298 | first->current.border != B_NORMAL) { | ||
299 | title_bar_height = 0; | ||
300 | } | ||
301 | |||
302 | double w = (double) width / children->length; | ||
303 | int title_offset = 0; | ||
304 | for (int i = 0; i < children->length; i++) { | ||
305 | struct sway_container *child = children->items[i]; | ||
306 | bool activated = child == active; | ||
307 | int next_title_offset = round(w * i + w); | ||
308 | |||
309 | arrange_title_bar(child, title_offset, -title_bar_height, | ||
310 | next_title_offset - title_offset, title_bar_height); | ||
311 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
312 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); | ||
313 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
314 | |||
315 | if (activated) { | ||
316 | arrange_container(child, width, height - title_bar_height, | ||
317 | false, 0); | ||
318 | } else { | ||
319 | disable_container(child); | ||
320 | } | ||
321 | |||
322 | title_offset = next_title_offset; | ||
323 | } | ||
324 | } else if (layout == L_STACKED) { | ||
325 | struct sway_container *first = children->length == 1 ? | ||
326 | ((struct sway_container *)children->items[0]) : NULL; | ||
327 | if (config->hide_lone_tab && first && first->view && | ||
328 | first->current.border != B_NORMAL) { | ||
329 | title_bar_height = 0; | ||
330 | } | ||
331 | |||
332 | int title_height = title_bar_height * children->length; | ||
333 | |||
334 | int y = 0; | ||
335 | for (int i = 0; i < children->length; i++) { | ||
336 | struct sway_container *child = children->items[i]; | ||
337 | bool activated = child == active; | ||
338 | |||
339 | arrange_title_bar(child, 0, y - title_height, width, title_bar_height); | ||
340 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
341 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); | ||
342 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
343 | |||
344 | if (activated) { | ||
345 | arrange_container(child, width, height - title_height, | ||
346 | false, 0); | ||
347 | } else { | ||
348 | disable_container(child); | ||
349 | } | ||
350 | |||
351 | y += title_bar_height; | ||
352 | } | ||
353 | } else if (layout == L_VERT) { | ||
354 | int off = 0; | ||
355 | for (int i = 0; i < children->length; i++) { | ||
356 | struct sway_container *child = children->items[i]; | ||
357 | int cheight = child->current.height; | ||
358 | |||
359 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
360 | wlr_scene_node_set_position(&child->scene_tree->node, 0, off); | ||
361 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
362 | arrange_container(child, width, cheight, true, gaps); | ||
363 | off += cheight + gaps; | ||
364 | } | ||
365 | } else if (layout == L_HORIZ) { | ||
366 | int off = 0; | ||
367 | for (int i = 0; i < children->length; i++) { | ||
368 | struct sway_container *child = children->items[i]; | ||
369 | int cwidth = child->current.width; | ||
370 | |||
371 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
372 | wlr_scene_node_set_position(&child->scene_tree->node, off, 0); | ||
373 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
374 | arrange_container(child, cwidth, height, true, gaps); | ||
375 | off += cwidth + gaps; | ||
376 | } | ||
377 | } else { | ||
378 | sway_assert(false, "unreachable"); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | static void arrange_container(struct sway_container *con, | ||
383 | int width, int height, bool title_bar, int gaps) { | ||
384 | // this container might have previously been in the scratchpad, | ||
385 | // make sure it's enabled for viewing | ||
386 | wlr_scene_node_set_enabled(&con->scene_tree->node, true); | ||
387 | |||
388 | if (con->view) { | ||
389 | int border_top = container_titlebar_height(); | ||
390 | int border_width = con->current.border_thickness; | ||
391 | |||
392 | if (title_bar && con->current.border != B_NORMAL) { | ||
393 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
394 | wlr_scene_node_set_enabled(&con->border.top->node, true); | ||
395 | } else { | ||
396 | wlr_scene_node_set_enabled(&con->border.top->node, false); | ||
397 | } | ||
398 | |||
399 | if (con->current.border == B_NORMAL) { | ||
400 | if (title_bar) { | ||
401 | arrange_title_bar(con, 0, 0, width, border_top); | ||
402 | } else { | ||
403 | border_top = 0; | ||
404 | // should be handled by the parent container | ||
405 | } | ||
406 | } else if (con->current.border == B_PIXEL) { | ||
407 | container_update(con); | ||
408 | border_top = title_bar && con->current.border_top ? border_width : 0; | ||
409 | } else if (con->current.border == B_NONE) { | ||
410 | container_update(con); | ||
411 | border_top = 0; | ||
412 | border_width = 0; | ||
413 | } else if (con->current.border == B_CSD) { | ||
414 | border_top = 0; | ||
415 | border_width = 0; | ||
416 | } else { | ||
417 | sway_assert(false, "unreachable"); | ||
418 | } | ||
419 | |||
420 | int border_bottom = con->current.border_bottom ? border_width : 0; | ||
421 | int border_left = con->current.border_left ? border_width : 0; | ||
422 | int border_right = con->current.border_right ? border_width : 0; | ||
423 | |||
424 | wlr_scene_rect_set_size(con->border.top, width, border_top); | ||
425 | wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); | ||
426 | wlr_scene_rect_set_size(con->border.left, | ||
427 | border_left, height - border_top - border_bottom); | ||
428 | wlr_scene_rect_set_size(con->border.right, | ||
429 | border_right, height - border_top - border_bottom); | ||
430 | |||
431 | wlr_scene_node_set_position(&con->border.top->node, 0, 0); | ||
432 | wlr_scene_node_set_position(&con->border.bottom->node, | ||
433 | 0, height - border_bottom); | ||
434 | wlr_scene_node_set_position(&con->border.left->node, | ||
435 | 0, border_top); | ||
436 | wlr_scene_node_set_position(&con->border.right->node, | ||
437 | width - border_right, border_top); | ||
438 | |||
439 | // make sure to reparent, it's possible that the client just came out of | ||
440 | // fullscreen mode where the parent of the surface is not the container | ||
441 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
442 | wlr_scene_node_set_position(&con->view->scene_tree->node, | ||
443 | border_left, border_top); | ||
444 | } else { | ||
445 | // make sure to disable the title bar if the parent is not managing it | ||
446 | if (title_bar) { | ||
447 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
448 | } | ||
449 | |||
450 | arrange_children(con->current.layout, con->current.children, | ||
451 | con->current.focused_inactive_child, con->content_tree, | ||
452 | width, height, gaps); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | static int container_get_gaps(struct sway_container *con) { | ||
457 | struct sway_workspace *ws = con->current.workspace; | ||
458 | struct sway_container *temp = con; | ||
459 | while (temp) { | ||
460 | enum sway_container_layout layout; | ||
461 | if (temp->current.parent) { | ||
462 | layout = temp->current.parent->current.layout; | ||
463 | } else { | ||
464 | layout = ws->current.layout; | ||
465 | } | ||
466 | if (layout == L_TABBED || layout == L_STACKED) { | ||
467 | return 0; | ||
468 | } | ||
469 | temp = temp->pending.parent; | ||
470 | } | ||
471 | return ws->gaps_inner; | ||
472 | } | ||
473 | |||
474 | static void arrange_fullscreen(struct wlr_scene_tree *tree, | ||
475 | struct sway_container *fs, struct sway_workspace *ws, | ||
476 | int width, int height) { | ||
477 | struct wlr_scene_node *fs_node; | ||
478 | if (fs->view) { | ||
479 | fs_node = &fs->view->scene_tree->node; | ||
480 | |||
481 | // if we only care about the view, disable any decorations | ||
482 | wlr_scene_node_set_enabled(&fs->scene_tree->node, false); | ||
483 | } else { | ||
484 | fs_node = &fs->scene_tree->node; | ||
485 | arrange_container(fs, width, height, true, container_get_gaps(fs)); | ||
486 | } | ||
487 | |||
488 | wlr_scene_node_reparent(fs_node, tree); | ||
489 | wlr_scene_node_lower_to_bottom(fs_node); | ||
490 | wlr_scene_node_set_position(fs_node, 0, 0); | ||
491 | } | ||
492 | |||
493 | static void arrange_workspace_floating(struct sway_workspace *ws) { | ||
494 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
495 | struct sway_container *floater = ws->current.floating->items[i]; | ||
496 | struct wlr_scene_tree *layer = root->layers.floating; | ||
497 | |||
498 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
499 | continue; | ||
500 | } | ||
501 | |||
502 | if (root->fullscreen_global) { | ||
503 | if (container_is_transient_for(floater, root->fullscreen_global)) { | ||
504 | layer = root->layers.fullscreen_global; | ||
505 | } | ||
506 | } else { | ||
507 | for (int i = 0; i < root->outputs->length; i++) { | ||
508 | struct sway_output *output = root->outputs->items[i]; | ||
509 | struct sway_workspace *active = output->current.active_workspace; | ||
510 | |||
511 | if (active && active->fullscreen && | ||
512 | container_is_transient_for(floater, active->fullscreen)) { | ||
513 | layer = root->layers.fullscreen; | ||
514 | } | ||
515 | } | ||
516 | } | ||
517 | |||
518 | wlr_scene_node_reparent(&floater->scene_tree->node, layer); | ||
519 | wlr_scene_node_set_position(&floater->scene_tree->node, | ||
520 | floater->current.x, floater->current.y); | ||
521 | wlr_scene_node_set_enabled(&floater->scene_tree->node, true); | ||
522 | |||
523 | arrange_container(floater, floater->current.width, floater->current.height, | ||
524 | true, ws->gaps_inner); | ||
525 | } | ||
526 | } | ||
527 | |||
528 | static void arrange_workspace_tiling(struct sway_workspace *ws, | ||
529 | int width, int height) { | ||
530 | arrange_children(ws->current.layout, ws->current.tiling, | ||
531 | ws->current.focused_inactive_child, ws->layers.tiling, | ||
532 | width, height, ws->gaps_inner); | ||
533 | } | ||
534 | |||
535 | static void disable_workspace(struct sway_workspace *ws) { | ||
536 | // if any containers were just moved to a disabled workspace it will | ||
537 | // have the parent of the old workspace. Move the workspace so that it won't | ||
538 | // be shown. | ||
539 | for (int i = 0; i < ws->current.tiling->length; i++) { | ||
540 | struct sway_container *child = ws->current.tiling->items[i]; | ||
541 | |||
542 | wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); | ||
543 | disable_container(child); | ||
544 | } | ||
545 | |||
546 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
547 | struct sway_container *floater = ws->current.floating->items[i]; | ||
548 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
549 | disable_container(floater); | ||
550 | wlr_scene_node_set_enabled(&floater->scene_tree->node, false); | ||
551 | } | ||
552 | } | ||
553 | |||
554 | static void arrange_output(struct sway_output *output, int width, int height) { | ||
555 | for (int i = 0; i < output->current.workspaces->length; i++) { | ||
556 | struct sway_workspace *child = output->current.workspaces->items[i]; | ||
557 | |||
558 | bool activated = output->current.active_workspace == child; | ||
559 | |||
560 | wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); | ||
561 | wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); | ||
562 | |||
563 | for (int i = 0; i < child->current.floating->length; i++) { | ||
564 | struct sway_container *floater = child->current.floating->items[i]; | ||
565 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
566 | wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); | ||
567 | } | ||
568 | |||
569 | if (activated) { | ||
570 | struct sway_container *fs = child->current.fullscreen; | ||
571 | wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); | ||
572 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); | ||
573 | |||
574 | arrange_workspace_floating(child); | ||
575 | |||
576 | wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); | ||
577 | wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); | ||
578 | wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); | ||
579 | |||
580 | if (fs) { | ||
581 | wlr_scene_rect_set_size(output->fullscreen_background, width, height); | ||
582 | |||
583 | arrange_fullscreen(child->layers.fullscreen, fs, child, | ||
584 | width, height); | ||
585 | } else { | ||
586 | struct wlr_box *area = &output->usable_area; | ||
587 | struct side_gaps *gaps = &child->current_gaps; | ||
588 | |||
589 | wlr_scene_node_set_position(&child->layers.tiling->node, | ||
590 | gaps->left + area->x, gaps->top + area->y); | ||
591 | |||
592 | arrange_workspace_tiling(child, | ||
593 | area->width - gaps->left - gaps->right, | ||
594 | area->height - gaps->top - gaps->bottom); | ||
595 | } | ||
596 | } else { | ||
597 | wlr_scene_node_set_enabled(&child->layers.tiling->node, false); | ||
598 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); | ||
599 | |||
600 | disable_workspace(child); | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | |||
605 | static void arrange_popup(struct wlr_scene_tree *popup) { | ||
606 | struct wlr_scene_node *node; | ||
607 | wl_list_for_each(node, &popup->children, link) { | ||
608 | struct sway_xdg_popup *popup = scene_descriptor_try_get(node, | ||
609 | SWAY_SCENE_DESC_POPUP); | ||
610 | |||
611 | // the popup layer may have popups from layer_shell surfaces, in this | ||
612 | // case those don't have a scene descriptor, so lets skip those here. | ||
613 | if (popup) { | ||
614 | struct wlr_scene_tree *tree = popup->view->content_tree; | ||
615 | |||
616 | int lx, ly; | ||
617 | wlr_scene_node_coords(&tree->node, &lx, &ly); | ||
618 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
619 | } | ||
620 | } | ||
621 | } | ||
622 | |||
623 | static void arrange_root(struct sway_root *root) { | ||
624 | struct sway_container *fs = root->fullscreen_global; | ||
625 | |||
626 | wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); | ||
627 | wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); | ||
628 | wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); | ||
629 | wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); | ||
630 | wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); | ||
631 | wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); | ||
632 | |||
633 | // hide all contents in the scratchpad | ||
634 | for (int i = 0; i < root->scratchpad->length; i++) { | ||
635 | struct sway_container *con = root->scratchpad->items[i]; | ||
636 | |||
637 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); | ||
638 | } | ||
639 | |||
640 | if (fs) { | ||
641 | for (int i = 0; i < root->outputs->length; i++) { | ||
642 | struct sway_output *output = root->outputs->items[i]; | ||
643 | struct sway_workspace *ws = output->current.active_workspace; | ||
644 | |||
645 | if (ws) { | ||
646 | arrange_workspace_floating(ws); | ||
647 | } | ||
648 | } | ||
649 | |||
650 | arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, | ||
651 | root->width, root->height); | ||
652 | } else { | ||
653 | for (int i = 0; i < root->outputs->length; i++) { | ||
654 | struct sway_output *output = root->outputs->items[i]; | ||
655 | |||
656 | wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); | ||
657 | |||
658 | wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); | ||
659 | wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); | ||
660 | wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); | ||
661 | wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); | ||
662 | wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); | ||
663 | wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); | ||
664 | wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); | ||
665 | |||
666 | wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); | ||
667 | wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); | ||
668 | wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); | ||
669 | wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); | ||
670 | wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); | ||
671 | wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); | ||
672 | wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); | ||
673 | |||
674 | arrange_output(output, output->width, output->height); | ||
675 | } | ||
676 | } | ||
677 | |||
678 | arrange_popup(root->layers.popup); | ||
679 | } | ||
680 | |||
255 | /** | 681 | /** |
256 | * Apply a transaction to the "current" state of the tree. | 682 | * Apply a transaction to the "current" state of the tree. |
257 | */ | 683 | */ |
@@ -291,8 +717,6 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
291 | 717 | ||
292 | node->instruction = NULL; | 718 | node->instruction = NULL; |
293 | } | 719 | } |
294 | |||
295 | cursor_rebase_all(); | ||
296 | } | 720 | } |
297 | 721 | ||
298 | static void transaction_commit_pending(void); | 722 | static void transaction_commit_pending(void); |
@@ -305,6 +729,8 @@ static void transaction_progress(void) { | |||
305 | return; | 729 | return; |
306 | } | 730 | } |
307 | transaction_apply(server.queued_transaction); | 731 | transaction_apply(server.queued_transaction); |
732 | arrange_root(root); | ||
733 | cursor_rebase_all(); | ||
308 | transaction_destroy(server.queued_transaction); | 734 | transaction_destroy(server.queued_transaction); |
309 | server.queued_transaction = NULL; | 735 | server.queued_transaction = NULL; |
310 | 736 | ||