diff options
Diffstat (limited to 'sway/tree/view.c')
-rw-r--r-- | sway/tree/view.c | 609 |
1 files changed, 225 insertions, 384 deletions
diff --git a/sway/tree/view.c b/sway/tree/view.c index fcdd06f7..35b4b73f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,11 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
5 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
6 | #include <wlr/types/wlr_buffer.h> | 5 | #include <wlr/types/wlr_buffer.h> |
6 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
7 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 9 | #include <wlr/types/wlr_server_decoration.h> |
10 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 11 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
10 | #include "config.h" | 12 | #include "config.h" |
11 | #if HAVE_XWAYLAND | 13 | #if HAVE_XWAYLAND |
@@ -15,14 +17,16 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
17 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
20 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
21 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
22 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
23 | #include "sway/output.h" | 25 | #include "sway/output.h" |
24 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
26 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
28 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -32,15 +36,29 @@ | |||
32 | #include "pango.h" | 36 | #include "pango.h" |
33 | #include "stringop.h" | 37 | #include "stringop.h" |
34 | 38 | ||
35 | void view_init(struct sway_view *view, enum sway_view_type type, | 39 | bool view_init(struct sway_view *view, enum sway_view_type type, |
36 | const struct sway_view_impl *impl) { | 40 | const struct sway_view_impl *impl) { |
41 | bool failed = false; | ||
42 | view->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
43 | view->content_tree = alloc_scene_tree(view->scene_tree, &failed); | ||
44 | |||
45 | if (!failed && !scene_descriptor_assign(&view->scene_tree->node, | ||
46 | SWAY_SCENE_DESC_VIEW, view)) { | ||
47 | failed = true; | ||
48 | } | ||
49 | |||
50 | if (failed) { | ||
51 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
52 | return false; | ||
53 | } | ||
54 | |||
37 | view->type = type; | 55 | view->type = type; |
38 | view->impl = impl; | 56 | view->impl = impl; |
39 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
40 | wl_list_init(&view->saved_buffers); | ||
41 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
42 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
43 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
44 | } | 62 | } |
45 | 63 | ||
46 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -57,11 +75,10 @@ void view_destroy(struct sway_view *view) { | |||
57 | return; | 75 | return; |
58 | } | 76 | } |
59 | wl_list_remove(&view->events.unmap.listener_list); | 77 | wl_list_remove(&view->events.unmap.listener_list); |
60 | if (!wl_list_empty(&view->saved_buffers)) { | ||
61 | view_remove_saved_buffer(view); | ||
62 | } | ||
63 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
64 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
65 | free(view->title_format); | 82 | free(view->title_format); |
66 | 83 | ||
67 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -362,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
362 | } | 379 | } |
363 | } | 380 | } |
364 | 381 | ||
365 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
366 | struct sway_workspace *ws = view->container->pending.workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
367 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
368 | return; | 385 | seat = input_manager_current_seat(); |
369 | } | 386 | } |
370 | struct sway_seat *seat = input_manager_current_seat(); | ||
371 | 387 | ||
372 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
373 | case FOWA_SMART: | 389 | case FOWA_SMART: |
374 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
375 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
376 | } else { | 393 | } else { |
377 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
378 | } | 395 | } |
@@ -381,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
381 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
382 | break; | 399 | break; |
383 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
384 | seat_set_focus_container(seat, view->container); | 401 | if (container_is_scratchpad_hidden_or_child(view->container)) { |
402 | root_scratchpad_show(view->container); | ||
403 | } else { | ||
404 | seat_set_focus_container(seat, view->container); | ||
405 | container_raise_floating(view->container); | ||
406 | } | ||
385 | break; | 407 | break; |
386 | case FOWA_NONE: | 408 | case FOWA_NONE: |
387 | break; | 409 | break; |
388 | } | 410 | } |
411 | transaction_commit_dirty(); | ||
412 | } | ||
413 | |||
414 | void view_request_urgent(struct sway_view *view) { | ||
415 | if (config->focus_on_window_activation != FOWA_NONE) { | ||
416 | view_set_urgent(view, true); | ||
417 | } | ||
389 | } | 418 | } |
390 | 419 | ||
391 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { | 420 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { |
@@ -432,52 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
432 | } | 461 | } |
433 | } | 462 | } |
434 | 463 | ||
435 | void view_damage_from(struct sway_view *view) { | ||
436 | for (int i = 0; i < root->outputs->length; ++i) { | ||
437 | struct sway_output *output = root->outputs->items[i]; | ||
438 | output_damage_from_view(output, view); | ||
439 | } | ||
440 | } | ||
441 | |||
442 | void view_for_each_surface(struct sway_view *view, | ||
443 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
444 | if (!view->surface) { | ||
445 | return; | ||
446 | } | ||
447 | if (view->impl->for_each_surface) { | ||
448 | view->impl->for_each_surface(view, iterator, user_data); | ||
449 | } else { | ||
450 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | void view_for_each_popup_surface(struct sway_view *view, | ||
455 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
456 | if (!view->surface) { | ||
457 | return; | ||
458 | } | ||
459 | if (view->impl->for_each_popup_surface) { | ||
460 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | static void view_subsurface_create(struct sway_view *view, | ||
465 | struct wlr_subsurface *subsurface); | ||
466 | |||
467 | static void view_init_subsurfaces(struct sway_view *view, | ||
468 | struct wlr_surface *surface); | ||
469 | |||
470 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
471 | struct wlr_surface *surface); | ||
472 | |||
473 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
474 | void *data) { | ||
475 | struct sway_view *view = | ||
476 | wl_container_of(listener, view, surface_new_subsurface); | ||
477 | struct wlr_subsurface *subsurface = data; | ||
478 | view_subsurface_create(view, subsurface); | ||
479 | } | ||
480 | |||
481 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
482 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
483 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -519,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) { | |||
519 | #if HAVE_XWAYLAND | 502 | #if HAVE_XWAYLAND |
520 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
521 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
522 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
523 | pid = surf->pid; | 506 | pid = surf->pid; |
524 | break; | 507 | break; |
525 | #endif | 508 | #endif |
@@ -532,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
532 | view->pid = pid; | 515 | view->pid = pid; |
533 | } | 516 | } |
534 | 517 | ||
518 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { | ||
519 | if (view->ctx) { | ||
520 | // This ctx has been replaced | ||
521 | launcher_ctx_destroy(view->ctx); | ||
522 | view->ctx = NULL; | ||
523 | } | ||
524 | if (ctx == NULL) { | ||
525 | return; | ||
526 | } | ||
527 | launcher_ctx_consume(ctx); | ||
528 | |||
529 | view->ctx = ctx; | ||
530 | } | ||
531 | |||
535 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
536 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
537 | 534 | ||
@@ -567,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
567 | } | 564 | } |
568 | list_free(criterias); | 565 | list_free(criterias); |
569 | if (ws) { | 566 | if (ws) { |
570 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
571 | return ws; | 568 | return ws; |
572 | } | 569 | } |
573 | 570 | ||
574 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
575 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
576 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
577 | return ws; | 575 | return ws; |
578 | } | 576 | } |
579 | 577 | ||
@@ -591,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
591 | return NULL; | 589 | return NULL; |
592 | } | 590 | } |
593 | 591 | ||
592 | static void update_ext_foreign_toplevel(struct sway_view *view) { | ||
593 | struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = { | ||
594 | .app_id = view_get_app_id(view), | ||
595 | .title = view_get_title(view), | ||
596 | }; | ||
597 | wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state); | ||
598 | } | ||
599 | |||
594 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
595 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
596 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
@@ -716,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
716 | view_populate_pid(view); | 722 | view_populate_pid(view); |
717 | view->container = container_create(view); | 723 | view->container = container_create(view); |
718 | 724 | ||
725 | if (view->ctx == NULL) { | ||
726 | struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); | ||
727 | if (ctx != NULL) { | ||
728 | view_assign_ctx(view, ctx); | ||
729 | } | ||
730 | } | ||
731 | |||
719 | // If there is a request to be opened fullscreen on a specific output, try | 732 | // If there is a request to be opened fullscreen on a specific output, try |
720 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
721 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -729,10 +742,36 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
729 | } | 742 | } |
730 | 743 | ||
731 | struct sway_seat *seat = input_manager_current_seat(); | 744 | struct sway_seat *seat = input_manager_current_seat(); |
732 | struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) | 745 | struct sway_node *node = |
733 | : seat_get_focus_inactive(seat, &root->node); | 746 | seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); |
734 | struct sway_container *target_sibling = node->type == N_CONTAINER ? | 747 | struct sway_container *target_sibling = NULL; |
735 | node->sway_container : NULL; | 748 | if (node && node->type == N_CONTAINER) { |
749 | if (container_is_floating(node->sway_container)) { | ||
750 | // If we're about to launch the view into the floating container, then | ||
751 | // launch it as a tiled view instead. | ||
752 | if (ws) { | ||
753 | target_sibling = seat_get_focus_inactive_tiling(seat, ws); | ||
754 | if (target_sibling) { | ||
755 | struct sway_container *con = | ||
756 | seat_get_focus_inactive_view(seat, &target_sibling->node); | ||
757 | if (con) { | ||
758 | target_sibling = con; | ||
759 | } | ||
760 | } | ||
761 | } else { | ||
762 | ws = seat_get_last_known_workspace(seat); | ||
763 | } | ||
764 | } else { | ||
765 | target_sibling = node->sway_container; | ||
766 | } | ||
767 | } | ||
768 | |||
769 | struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { | ||
770 | .app_id = view_get_app_id(view), | ||
771 | .title = view_get_title(view), | ||
772 | }; | ||
773 | view->ext_foreign_toplevel = | ||
774 | wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); | ||
736 | 775 | ||
737 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
738 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
@@ -749,13 +788,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
749 | wl_signal_add(&view->foreign_toplevel->events.destroy, | 788 | wl_signal_add(&view->foreign_toplevel->events.destroy, |
750 | &view->foreign_destroy); | 789 | &view->foreign_destroy); |
751 | 790 | ||
752 | // If we're about to launch the view into the floating container, then | ||
753 | // launch it as a tiled view in the root of the workspace instead. | ||
754 | if (target_sibling && container_is_floating(target_sibling)) { | ||
755 | target_sibling = NULL; | ||
756 | ws = seat_get_last_known_workspace(seat); | ||
757 | } | ||
758 | |||
759 | struct sway_container *container = view->container; | 791 | struct sway_container *container = view->container; |
760 | if (target_sibling) { | 792 | if (target_sibling) { |
761 | container_add_sibling(target_sibling, container, 1); | 793 | container_add_sibling(target_sibling, container, 1); |
@@ -764,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
764 | } | 796 | } |
765 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
766 | 798 | ||
767 | view_init_subsurfaces(view, wlr_surface); | ||
768 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
769 | &view->surface_new_subsurface); | ||
770 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
771 | |||
772 | if (decoration) { | 799 | if (decoration) { |
773 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
774 | } | 801 | } |
@@ -812,9 +839,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
812 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
813 | 840 | ||
814 | #if HAVE_XWAYLAND | 841 | #if HAVE_XWAYLAND |
815 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
816 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
817 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
818 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
819 | WLR_ICCCM_INPUT_MODEL_NONE; | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
820 | } | 846 | } |
@@ -824,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
824 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
825 | } | 851 | } |
826 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
827 | const char *app_id; | 857 | const char *app_id; |
828 | const char *class; | 858 | const char *class; |
829 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
@@ -834,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
834 | } | 864 | } |
835 | 865 | ||
836 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
837 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
838 | 868 | ||
839 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
840 | 870 | ||
841 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
842 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
843 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
844 | } | 874 | } |
845 | 875 | ||
876 | if (view->ext_foreign_toplevel) { | ||
877 | wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel); | ||
878 | view->ext_foreign_toplevel = NULL; | ||
879 | } | ||
880 | |||
846 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
847 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
848 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
@@ -889,293 +924,47 @@ void view_update_size(struct sway_view *view) { | |||
889 | container_set_geometry_from_content(con); | 924 | container_set_geometry_from_content(con); |
890 | } | 925 | } |
891 | 926 | ||
892 | void view_center_surface(struct sway_view *view) { | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
893 | struct sway_container *con = view->container; | 928 | struct sway_container *con = view->container; |
894 | // We always center the current coordinates rather than the next, as the | ||
895 | // geometry immediately affects the currently active rendering. | ||
896 | con->surface_x = fmax(con->current.content_x, con->current.content_x + | ||
897 | (con->current.content_width - view->geometry.width) / 2); | ||
898 | con->surface_y = fmax(con->current.content_y, con->current.content_y + | ||
899 | (con->current.content_height - view->geometry.height) / 2); | ||
900 | } | ||
901 | |||
902 | static const struct sway_view_child_impl subsurface_impl; | ||
903 | 929 | ||
904 | static void subsurface_get_root_coords(struct sway_view_child *child, | 930 | if (container_is_floating(con)) { |
905 | int *root_sx, int *root_sy) { | 931 | // We always center the current coordinates rather than the next, as the |
906 | struct wlr_surface *surface = child->surface; | 932 | // geometry immediately affects the currently active rendering. |
907 | *root_sx = -child->view->geometry.x; | 933 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
908 | *root_sy = -child->view->geometry.y; | 934 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
909 | 935 | ||
910 | if (child->parent && child->parent->impl && | 936 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
911 | child->parent->impl->get_root_coords) { | ||
912 | int sx, sy; | ||
913 | child->parent->impl->get_root_coords(child->parent, &sx, &sy); | ||
914 | *root_sx += sx; | ||
915 | *root_sy += sy; | ||
916 | } else { | 937 | } else { |
917 | while (surface && wlr_surface_is_subsurface(surface)) { | 938 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
918 | struct wlr_subsurface *subsurface = | ||
919 | wlr_subsurface_from_wlr_surface(surface); | ||
920 | if (subsurface == NULL) { | ||
921 | break; | ||
922 | } | ||
923 | *root_sx += subsurface->current.x; | ||
924 | *root_sy += subsurface->current.y; | ||
925 | surface = subsurface->parent; | ||
926 | } | ||
927 | } | 939 | } |
928 | } | ||
929 | 940 | ||
930 | static void subsurface_destroy(struct sway_view_child *child) { | 941 | // only make sure to clip the content if there is content to clip |
931 | if (!sway_assert(child->impl == &subsurface_impl, | 942 | if (!wl_list_empty(&con->view->content_tree->children)) { |
932 | "Expected a subsurface")) { | 943 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ |
933 | return; | 944 | .x = con->view->geometry.x, |
934 | } | 945 | .y = con->view->geometry.y, |
935 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | 946 | .width = con->current.content_width, |
936 | wl_list_remove(&subsurface->destroy.link); | 947 | .height = con->current.content_height, |
937 | free(subsurface); | 948 | }); |
938 | } | ||
939 | |||
940 | static const struct sway_view_child_impl subsurface_impl = { | ||
941 | .get_root_coords = subsurface_get_root_coords, | ||
942 | .destroy = subsurface_destroy, | ||
943 | }; | ||
944 | |||
945 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
946 | void *data) { | ||
947 | struct sway_subsurface *subsurface = | ||
948 | wl_container_of(listener, subsurface, destroy); | ||
949 | struct sway_view_child *child = &subsurface->child; | ||
950 | view_child_destroy(child); | ||
951 | } | ||
952 | |||
953 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
954 | |||
955 | static void view_subsurface_create(struct sway_view *view, | ||
956 | struct wlr_subsurface *wlr_subsurface) { | ||
957 | struct sway_subsurface *subsurface = | ||
958 | calloc(1, sizeof(struct sway_subsurface)); | ||
959 | if (subsurface == NULL) { | ||
960 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
961 | return; | ||
962 | } | ||
963 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
964 | wlr_subsurface->surface); | ||
965 | |||
966 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
967 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
968 | |||
969 | subsurface->child.mapped = true; | ||
970 | |||
971 | view_child_damage(&subsurface->child, true); | ||
972 | } | ||
973 | |||
974 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
975 | struct wlr_subsurface *wlr_subsurface) { | ||
976 | struct sway_subsurface *subsurface = | ||
977 | calloc(1, sizeof(struct sway_subsurface)); | ||
978 | if (subsurface == NULL) { | ||
979 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
980 | return; | ||
981 | } | ||
982 | subsurface->child.parent = child; | ||
983 | wl_list_insert(&child->children, &subsurface->child.link); | ||
984 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
985 | wlr_subsurface->surface); | ||
986 | |||
987 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
988 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
989 | |||
990 | subsurface->child.mapped = true; | ||
991 | |||
992 | view_child_damage(&subsurface->child, true); | ||
993 | } | ||
994 | |||
995 | static bool view_child_is_mapped(struct sway_view_child *child) { | ||
996 | while (child) { | ||
997 | if (!child->mapped) { | ||
998 | return false; | ||
999 | } | ||
1000 | child = child->parent; | ||
1001 | } | ||
1002 | return true; | ||
1003 | } | ||
1004 | |||
1005 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
1006 | if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { | ||
1007 | return; | ||
1008 | } | ||
1009 | int sx, sy; | ||
1010 | child->impl->get_root_coords(child, &sx, &sy); | ||
1011 | desktop_damage_surface(child->surface, | ||
1012 | child->view->container->pending.content_x + sx, | ||
1013 | child->view->container->pending.content_y + sy, whole); | ||
1014 | } | ||
1015 | |||
1016 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
1017 | void *data) { | ||
1018 | struct sway_view_child *child = | ||
1019 | wl_container_of(listener, child, surface_commit); | ||
1020 | view_child_damage(child, false); | ||
1021 | } | ||
1022 | |||
1023 | static void view_child_handle_surface_new_subsurface( | ||
1024 | struct wl_listener *listener, void *data) { | ||
1025 | struct sway_view_child *child = | ||
1026 | wl_container_of(listener, child, surface_new_subsurface); | ||
1027 | struct wlr_subsurface *subsurface = data; | ||
1028 | view_child_subsurface_create(child, subsurface); | ||
1029 | } | ||
1030 | |||
1031 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
1032 | void *data) { | ||
1033 | struct sway_view_child *child = | ||
1034 | wl_container_of(listener, child, surface_destroy); | ||
1035 | view_child_destroy(child); | ||
1036 | } | ||
1037 | |||
1038 | static void view_init_subsurfaces(struct sway_view *view, | ||
1039 | struct wlr_surface *surface) { | ||
1040 | struct wlr_subsurface *subsurface; | ||
1041 | wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) { | ||
1042 | view_subsurface_create(view, subsurface); | ||
1043 | } | ||
1044 | wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) { | ||
1045 | view_subsurface_create(view, subsurface); | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
1050 | struct wlr_surface *surface) { | ||
1051 | struct wlr_subsurface *subsurface; | ||
1052 | wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) { | ||
1053 | view_child_subsurface_create(view_child, subsurface); | ||
1054 | } | ||
1055 | wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) { | ||
1056 | view_child_subsurface_create(view_child, subsurface); | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1060 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1061 | void *data) { | ||
1062 | struct sway_view_child *child = | ||
1063 | wl_container_of(listener, child, surface_map); | ||
1064 | child->mapped = true; | ||
1065 | view_child_damage(child, true); | ||
1066 | } | ||
1067 | |||
1068 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1069 | void *data) { | ||
1070 | struct sway_view_child *child = | ||
1071 | wl_container_of(listener, child, surface_unmap); | ||
1072 | view_child_damage(child, true); | ||
1073 | child->mapped = false; | ||
1074 | } | ||
1075 | |||
1076 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1077 | void *data) { | ||
1078 | struct sway_view_child *child = | ||
1079 | wl_container_of(listener, child, view_unmap); | ||
1080 | view_child_damage(child, true); | ||
1081 | child->mapped = false; | ||
1082 | } | ||
1083 | |||
1084 | void view_child_init(struct sway_view_child *child, | ||
1085 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1086 | struct wlr_surface *surface) { | ||
1087 | child->impl = impl; | ||
1088 | child->view = view; | ||
1089 | child->surface = surface; | ||
1090 | wl_list_init(&child->children); | ||
1091 | |||
1092 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1093 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1094 | wl_signal_add(&surface->events.new_subsurface, | ||
1095 | &child->surface_new_subsurface); | ||
1096 | child->surface_new_subsurface.notify = | ||
1097 | view_child_handle_surface_new_subsurface; | ||
1098 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1099 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1100 | |||
1101 | // Not all child views have a map/unmap event | ||
1102 | child->surface_map.notify = view_child_handle_surface_map; | ||
1103 | wl_list_init(&child->surface_map.link); | ||
1104 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1105 | wl_list_init(&child->surface_unmap.link); | ||
1106 | |||
1107 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1108 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1109 | |||
1110 | struct sway_workspace *workspace = child->view->container->pending.workspace; | ||
1111 | if (workspace) { | ||
1112 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1113 | } | ||
1114 | |||
1115 | view_child_init_subsurfaces(child, surface); | ||
1116 | } | ||
1117 | |||
1118 | void view_child_destroy(struct sway_view_child *child) { | ||
1119 | if (view_child_is_mapped(child) && child->view->container != NULL) { | ||
1120 | view_child_damage(child, true); | ||
1121 | } | ||
1122 | |||
1123 | if (child->parent != NULL) { | ||
1124 | wl_list_remove(&child->link); | ||
1125 | child->parent = NULL; | ||
1126 | } | ||
1127 | |||
1128 | struct sway_view_child *subchild, *tmpchild; | ||
1129 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1130 | wl_list_remove(&subchild->link); | ||
1131 | subchild->parent = NULL; | ||
1132 | // The subchild lost its parent link, so it cannot see that the parent | ||
1133 | // is unmapped. Unmap it directly. | ||
1134 | subchild->mapped = false; | ||
1135 | } | ||
1136 | |||
1137 | wl_list_remove(&child->surface_commit.link); | ||
1138 | wl_list_remove(&child->surface_destroy.link); | ||
1139 | wl_list_remove(&child->surface_map.link); | ||
1140 | wl_list_remove(&child->surface_unmap.link); | ||
1141 | wl_list_remove(&child->view_unmap.link); | ||
1142 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1143 | |||
1144 | if (child->impl && child->impl->destroy) { | ||
1145 | child->impl->destroy(child); | ||
1146 | } else { | ||
1147 | free(child); | ||
1148 | } | 949 | } |
1149 | } | 950 | } |
1150 | 951 | ||
1151 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | 952 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { |
1152 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 953 | struct wlr_xdg_surface *xdg_surface; |
1153 | struct wlr_xdg_surface *xdg_surface = | 954 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1154 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1155 | if (xdg_surface == NULL) { | ||
1156 | return NULL; | ||
1157 | } | ||
1158 | return view_from_wlr_xdg_surface(xdg_surface); | 955 | return view_from_wlr_xdg_surface(xdg_surface); |
1159 | } | 956 | } |
1160 | #if HAVE_XWAYLAND | 957 | #if HAVE_XWAYLAND |
1161 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 958 | struct wlr_xwayland_surface *xsurface; |
1162 | struct wlr_xwayland_surface *xsurface = | 959 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1163 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1164 | if (xsurface == NULL) { | ||
1165 | return NULL; | ||
1166 | } | ||
1167 | return view_from_wlr_xwayland_surface(xsurface); | 960 | return view_from_wlr_xwayland_surface(xsurface); |
1168 | } | 961 | } |
1169 | #endif | 962 | #endif |
1170 | if (wlr_surface_is_subsurface(wlr_surface)) { | 963 | struct wlr_subsurface *subsurface; |
1171 | struct wlr_subsurface *subsurface = | 964 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1172 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1173 | if (subsurface == NULL) { | ||
1174 | return NULL; | ||
1175 | } | ||
1176 | return view_from_wlr_surface(subsurface->parent); | 965 | return view_from_wlr_surface(subsurface->parent); |
1177 | } | 966 | } |
1178 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 967 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1179 | return NULL; | 968 | return NULL; |
1180 | } | 969 | } |
1181 | 970 | ||
@@ -1256,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1256 | return len; | 1045 | return len; |
1257 | } | 1046 | } |
1258 | 1047 | ||
1048 | void view_update_app_id(struct sway_view *view) { | ||
1049 | const char *app_id = view_get_app_id(view); | ||
1050 | |||
1051 | if (view->foreign_toplevel && app_id) { | ||
1052 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); | ||
1053 | } | ||
1054 | |||
1055 | if (view->ext_foreign_toplevel) { | ||
1056 | update_ext_foreign_toplevel(view); | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1259 | void view_update_title(struct sway_view *view, bool force) { | 1060 | void view_update_title(struct sway_view *view, bool force) { |
1260 | const char *title = view_get_title(view); | 1061 | const char *title = view_get_title(view); |
1261 | 1062 | ||
@@ -1271,31 +1072,41 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1271 | 1072 | ||
1272 | free(view->container->title); | 1073 | free(view->container->title); |
1273 | free(view->container->formatted_title); | 1074 | free(view->container->formatted_title); |
1274 | if (title) { | 1075 | |
1275 | size_t len = parse_title_format(view, NULL); | 1076 | size_t len = parse_title_format(view, NULL); |
1077 | |||
1078 | if (len) { | ||
1276 | char *buffer = calloc(len + 1, sizeof(char)); | 1079 | char *buffer = calloc(len + 1, sizeof(char)); |
1277 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1080 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1278 | return; | 1081 | return; |
1279 | } | 1082 | } |
1280 | parse_title_format(view, buffer); | ||
1281 | 1083 | ||
1282 | view->container->title = strdup(title); | 1084 | parse_title_format(view, buffer); |
1283 | view->container->formatted_title = buffer; | 1085 | view->container->formatted_title = buffer; |
1284 | } else { | 1086 | } else { |
1285 | view->container->title = NULL; | ||
1286 | view->container->formatted_title = NULL; | 1087 | view->container->formatted_title = NULL; |
1287 | } | 1088 | } |
1288 | container_calculate_title_height(view->container); | 1089 | |
1289 | config_update_font_height(false); | 1090 | view->container->title = title ? strdup(title) : NULL; |
1290 | 1091 | ||
1291 | // Update title after the global font height is updated | 1092 | // Update title after the global font height is updated |
1292 | container_update_title_textures(view->container); | 1093 | if (view->container->title_bar.title_text && len) { |
1094 | sway_text_node_set_text(view->container->title_bar.title_text, | ||
1095 | view->container->formatted_title); | ||
1096 | container_arrange_title_bar(view->container); | ||
1097 | } else { | ||
1098 | container_update_title_bar(view->container); | ||
1099 | } | ||
1293 | 1100 | ||
1294 | ipc_event_window(view->container, "title"); | 1101 | ipc_event_window(view->container, "title"); |
1295 | 1102 | ||
1296 | if (view->foreign_toplevel && title) { | 1103 | if (view->foreign_toplevel && title) { |
1297 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1104 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1298 | } | 1105 | } |
1106 | |||
1107 | if (view->ext_foreign_toplevel) { | ||
1108 | update_ext_foreign_toplevel(view); | ||
1109 | } | ||
1299 | } | 1110 | } |
1300 | 1111 | ||
1301 | bool view_is_visible(struct sway_view *view) { | 1112 | bool view_is_visible(struct sway_view *view) { |
@@ -1356,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1356 | return; | 1167 | return; |
1357 | } | 1168 | } |
1358 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1169 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1170 | container_update_itself_and_parents(view->container); | ||
1359 | } else { | 1171 | } else { |
1360 | view->urgent = (struct timespec){ 0 }; | 1172 | view->urgent = (struct timespec){ 0 }; |
1361 | if (view->urgent_timer) { | 1173 | if (view->urgent_timer) { |
@@ -1363,7 +1175,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1363 | view->urgent_timer = NULL; | 1175 | view->urgent_timer = NULL; |
1364 | } | 1176 | } |
1365 | } | 1177 | } |
1366 | container_damage_whole(view->container); | ||
1367 | 1178 | ||
1368 | ipc_event_window(view->container, "urgent"); | 1179 | ipc_event_window(view->container, "urgent"); |
1369 | 1180 | ||
@@ -1377,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1377 | } | 1188 | } |
1378 | 1189 | ||
1379 | void view_remove_saved_buffer(struct sway_view *view) { | 1190 | void view_remove_saved_buffer(struct sway_view *view) { |
1380 | if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { | 1191 | if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { |
1381 | return; | 1192 | return; |
1382 | } | 1193 | } |
1383 | struct sway_saved_buffer *saved_buf, *tmp; | 1194 | |
1384 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1195 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1385 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1196 | view->saved_surface_tree = NULL; |
1386 | wl_list_remove(&saved_buf->link); | 1197 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1387 | free(saved_buf); | ||
1388 | } | ||
1389 | } | 1198 | } |
1390 | 1199 | ||
1391 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1200 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1392 | int sx, int sy, void *data) { | 1201 | int sx, int sy, void *data) { |
1393 | struct sway_view *view = data; | 1202 | struct wlr_scene_tree *tree = data; |
1394 | 1203 | ||
1395 | if (surface && wlr_surface_has_buffer(surface)) { | 1204 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1396 | wlr_buffer_lock(&surface->buffer->base); | 1205 | if (!sbuf) { |
1397 | struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); | 1206 | sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); |
1398 | saved_buffer->buffer = surface->buffer; | 1207 | return; |
1399 | saved_buffer->width = surface->current.width; | ||
1400 | saved_buffer->height = surface->current.height; | ||
1401 | saved_buffer->x = view->container->surface_x + sx; | ||
1402 | saved_buffer->y = view->container->surface_y + sy; | ||
1403 | saved_buffer->transform = surface->current.transform; | ||
1404 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1405 | wl_list_insert(&view->saved_buffers, &saved_buffer->link); | ||
1406 | } | 1208 | } |
1209 | |||
1210 | wlr_scene_buffer_set_dest_size(sbuf, | ||
1211 | buffer->dst_width, buffer->dst_height); | ||
1212 | wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); | ||
1213 | wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); | ||
1214 | wlr_scene_node_set_position(&sbuf->node, sx, sy); | ||
1215 | wlr_scene_buffer_set_transform(sbuf, buffer->transform); | ||
1216 | wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); | ||
1407 | } | 1217 | } |
1408 | 1218 | ||
1409 | void view_save_buffer(struct sway_view *view) { | 1219 | void view_save_buffer(struct sway_view *view) { |
1410 | if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { | 1220 | if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { |
1411 | view_remove_saved_buffer(view); | 1221 | view_remove_saved_buffer(view); |
1412 | } | 1222 | } |
1413 | view_for_each_surface(view, view_save_buffer_iterator, view); | 1223 | |
1224 | view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); | ||
1225 | if (!view->saved_surface_tree) { | ||
1226 | sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); | ||
1227 | return; | ||
1228 | } | ||
1229 | |||
1230 | // Enable and disable the saved surface tree like so to atomitaclly update | ||
1231 | // the tree. This will prevent over damaging or other weirdness. | ||
1232 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); | ||
1233 | |||
1234 | wlr_scene_node_for_each_buffer(&view->content_tree->node, | ||
1235 | view_save_buffer_iterator, view->saved_surface_tree); | ||
1236 | |||
1237 | wlr_scene_node_set_enabled(&view->content_tree->node, false); | ||
1238 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); | ||
1414 | } | 1239 | } |
1415 | 1240 | ||
1416 | bool view_is_transient_for(struct sway_view *child, | 1241 | bool view_is_transient_for(struct sway_view *child, |
@@ -1418,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1418 | return child->impl->is_transient_for && | 1243 | return child->impl->is_transient_for && |
1419 | child->impl->is_transient_for(child, ancestor); | 1244 | child->impl->is_transient_for(child, ancestor); |
1420 | } | 1245 | } |
1246 | |||
1247 | static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, | ||
1248 | int x, int y, void *data) { | ||
1249 | struct timespec *when = data; | ||
1250 | wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); | ||
1251 | } | ||
1252 | |||
1253 | void view_send_frame_done(struct sway_view *view) { | ||
1254 | struct timespec when; | ||
1255 | clock_gettime(CLOCK_MONOTONIC, &when); | ||
1256 | |||
1257 | struct wlr_scene_node *node; | ||
1258 | wl_list_for_each(node, &view->content_tree->children, link) { | ||
1259 | wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); | ||
1260 | } | ||
1261 | } | ||