diff options
Diffstat (limited to 'sway/tree/view.c')
-rw-r--r-- | sway/tree/view.c | 570 |
1 files changed, 201 insertions, 369 deletions
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7d9e038d..35b4b73f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,9 +1,10 @@ | |||
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> |
9 | #include <wlr/types/wlr_subcompositor.h> | 10 | #include <wlr/types/wlr_subcompositor.h> |
@@ -16,14 +17,16 @@ | |||
16 | #include "log.h" | 17 | #include "log.h" |
17 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
18 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
19 | #include "sway/desktop.h" | ||
20 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
21 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
22 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
23 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
24 | #include "sway/output.h" | 25 | #include "sway/output.h" |
25 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
26 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
27 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
29 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -33,15 +36,29 @@ | |||
33 | #include "pango.h" | 36 | #include "pango.h" |
34 | #include "stringop.h" | 37 | #include "stringop.h" |
35 | 38 | ||
36 | 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, |
37 | 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 | |||
38 | view->type = type; | 55 | view->type = type; |
39 | view->impl = impl; | 56 | view->impl = impl; |
40 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
41 | wl_list_init(&view->saved_buffers); | ||
42 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
43 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
44 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
45 | } | 62 | } |
46 | 63 | ||
47 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -58,11 +75,10 @@ void view_destroy(struct sway_view *view) { | |||
58 | return; | 75 | return; |
59 | } | 76 | } |
60 | wl_list_remove(&view->events.unmap.listener_list); | 77 | wl_list_remove(&view->events.unmap.listener_list); |
61 | if (!wl_list_empty(&view->saved_buffers)) { | ||
62 | view_remove_saved_buffer(view); | ||
63 | } | ||
64 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
65 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
66 | free(view->title_format); | 82 | free(view->title_format); |
67 | 83 | ||
68 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -363,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
363 | } | 379 | } |
364 | } | 380 | } |
365 | 381 | ||
366 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
367 | struct sway_workspace *ws = view->container->pending.workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
368 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
369 | return; | 385 | seat = input_manager_current_seat(); |
370 | } | 386 | } |
371 | struct sway_seat *seat = input_manager_current_seat(); | ||
372 | 387 | ||
373 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
374 | case FOWA_SMART: | 389 | case FOWA_SMART: |
375 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
376 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
377 | } else { | 393 | } else { |
378 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
379 | } | 395 | } |
@@ -382,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
382 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
383 | break; | 399 | break; |
384 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
385 | 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 | } | ||
386 | break; | 407 | break; |
387 | case FOWA_NONE: | 408 | case FOWA_NONE: |
388 | break; | 409 | break; |
389 | } | 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 | } | ||
390 | } | 418 | } |
391 | 419 | ||
392 | 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) { |
@@ -433,52 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
433 | } | 461 | } |
434 | } | 462 | } |
435 | 463 | ||
436 | void view_damage_from(struct sway_view *view) { | ||
437 | for (int i = 0; i < root->outputs->length; ++i) { | ||
438 | struct sway_output *output = root->outputs->items[i]; | ||
439 | output_damage_from_view(output, view); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | void view_for_each_surface(struct sway_view *view, | ||
444 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
445 | if (!view->surface) { | ||
446 | return; | ||
447 | } | ||
448 | if (view->impl->for_each_surface) { | ||
449 | view->impl->for_each_surface(view, iterator, user_data); | ||
450 | } else { | ||
451 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | void view_for_each_popup_surface(struct sway_view *view, | ||
456 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
457 | if (!view->surface) { | ||
458 | return; | ||
459 | } | ||
460 | if (view->impl->for_each_popup_surface) { | ||
461 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static void view_subsurface_create(struct sway_view *view, | ||
466 | struct wlr_subsurface *subsurface); | ||
467 | |||
468 | static void view_init_subsurfaces(struct sway_view *view, | ||
469 | struct wlr_surface *surface); | ||
470 | |||
471 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
472 | struct wlr_surface *surface); | ||
473 | |||
474 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
475 | void *data) { | ||
476 | struct sway_view *view = | ||
477 | wl_container_of(listener, view, surface_new_subsurface); | ||
478 | struct wlr_subsurface *subsurface = data; | ||
479 | view_subsurface_create(view, subsurface); | ||
480 | } | ||
481 | |||
482 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
483 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
484 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -520,7 +502,7 @@ static void view_populate_pid(struct sway_view *view) { | |||
520 | #if HAVE_XWAYLAND | 502 | #if HAVE_XWAYLAND |
521 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
522 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
523 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
524 | pid = surf->pid; | 506 | pid = surf->pid; |
525 | break; | 507 | break; |
526 | #endif | 508 | #endif |
@@ -533,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
533 | view->pid = pid; | 515 | view->pid = pid; |
534 | } | 516 | } |
535 | 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 | |||
536 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
537 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
538 | 534 | ||
@@ -568,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
568 | } | 564 | } |
569 | list_free(criterias); | 565 | list_free(criterias); |
570 | if (ws) { | 566 | if (ws) { |
571 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
572 | return ws; | 568 | return ws; |
573 | } | 569 | } |
574 | 570 | ||
575 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
576 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
577 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
578 | return ws; | 575 | return ws; |
579 | } | 576 | } |
580 | 577 | ||
@@ -592,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
592 | return NULL; | 589 | return NULL; |
593 | } | 590 | } |
594 | 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 | |||
595 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
596 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
597 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
@@ -717,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
717 | view_populate_pid(view); | 722 | view_populate_pid(view); |
718 | view->container = container_create(view); | 723 | view->container = container_create(view); |
719 | 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 | |||
720 | // 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 |
721 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
722 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -754,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
754 | } | 766 | } |
755 | } | 767 | } |
756 | 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); | ||
775 | |||
757 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
758 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
759 | view->foreign_activate_request.notify = handle_foreign_activate_request; | 778 | view->foreign_activate_request.notify = handle_foreign_activate_request; |
@@ -777,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
777 | } | 796 | } |
778 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
779 | 798 | ||
780 | view_init_subsurfaces(view, wlr_surface); | ||
781 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
782 | &view->surface_new_subsurface); | ||
783 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
784 | |||
785 | if (decoration) { | 799 | if (decoration) { |
786 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
787 | } | 801 | } |
@@ -825,9 +839,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
825 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
826 | 840 | ||
827 | #if HAVE_XWAYLAND | 841 | #if HAVE_XWAYLAND |
828 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
829 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
830 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
831 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
832 | WLR_ICCCM_INPUT_MODEL_NONE; | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
833 | } | 846 | } |
@@ -837,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
837 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
838 | } | 851 | } |
839 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
840 | const char *app_id; | 857 | const char *app_id; |
841 | const char *class; | 858 | const char *class; |
842 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
@@ -847,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
847 | } | 864 | } |
848 | 865 | ||
849 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
850 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
851 | 868 | ||
852 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
853 | 870 | ||
854 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
855 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
856 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
857 | } | 874 | } |
858 | 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 | |||
859 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
860 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
861 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
@@ -902,291 +924,47 @@ void view_update_size(struct sway_view *view) { | |||
902 | container_set_geometry_from_content(con); | 924 | container_set_geometry_from_content(con); |
903 | } | 925 | } |
904 | 926 | ||
905 | void view_center_surface(struct sway_view *view) { | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
906 | struct sway_container *con = view->container; | 928 | struct sway_container *con = view->container; |
907 | // We always center the current coordinates rather than the next, as the | ||
908 | // geometry immediately affects the currently active rendering. | ||
909 | con->surface_x = fmax(con->current.content_x, con->current.content_x + | ||
910 | (con->current.content_width - view->geometry.width) / 2); | ||
911 | con->surface_y = fmax(con->current.content_y, con->current.content_y + | ||
912 | (con->current.content_height - view->geometry.height) / 2); | ||
913 | } | ||
914 | 929 | ||
915 | static const struct sway_view_child_impl subsurface_impl; | 930 | if (container_is_floating(con)) { |
931 | // We always center the current coordinates rather than the next, as the | ||
932 | // geometry immediately affects the currently active rendering. | ||
933 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); | ||
934 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); | ||
916 | 935 | ||
917 | static void subsurface_get_view_coords(struct sway_view_child *child, | 936 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
918 | int *sx, int *sy) { | ||
919 | struct wlr_surface *surface = child->surface; | ||
920 | if (child->parent && child->parent->impl && | ||
921 | child->parent->impl->get_view_coords) { | ||
922 | child->parent->impl->get_view_coords(child->parent, sx, sy); | ||
923 | } else { | 937 | } else { |
924 | *sx = *sy = 0; | 938 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
925 | } | 939 | } |
926 | struct wlr_subsurface *subsurface = | ||
927 | wlr_subsurface_from_wlr_surface(surface); | ||
928 | *sx += subsurface->current.x; | ||
929 | *sy += subsurface->current.y; | ||
930 | } | ||
931 | 940 | ||
932 | static void subsurface_destroy(struct sway_view_child *child) { | 941 | // only make sure to clip the content if there is content to clip |
933 | if (!sway_assert(child->impl == &subsurface_impl, | 942 | if (!wl_list_empty(&con->view->content_tree->children)) { |
934 | "Expected a subsurface")) { | 943 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ |
935 | return; | 944 | .x = con->view->geometry.x, |
936 | } | 945 | .y = con->view->geometry.y, |
937 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | 946 | .width = con->current.content_width, |
938 | wl_list_remove(&subsurface->destroy.link); | 947 | .height = con->current.content_height, |
939 | free(subsurface); | 948 | }); |
940 | } | ||
941 | |||
942 | static const struct sway_view_child_impl subsurface_impl = { | ||
943 | .get_view_coords = subsurface_get_view_coords, | ||
944 | .destroy = subsurface_destroy, | ||
945 | }; | ||
946 | |||
947 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
948 | void *data) { | ||
949 | struct sway_subsurface *subsurface = | ||
950 | wl_container_of(listener, subsurface, destroy); | ||
951 | struct sway_view_child *child = &subsurface->child; | ||
952 | view_child_destroy(child); | ||
953 | } | ||
954 | |||
955 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
956 | |||
957 | static void view_subsurface_create(struct sway_view *view, | ||
958 | struct wlr_subsurface *wlr_subsurface) { | ||
959 | struct sway_subsurface *subsurface = | ||
960 | calloc(1, sizeof(struct sway_subsurface)); | ||
961 | if (subsurface == NULL) { | ||
962 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
963 | return; | ||
964 | } | ||
965 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
966 | wlr_subsurface->surface); | ||
967 | |||
968 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
969 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
970 | |||
971 | subsurface->child.mapped = true; | ||
972 | |||
973 | view_child_damage(&subsurface->child, true); | ||
974 | } | ||
975 | |||
976 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
977 | struct wlr_subsurface *wlr_subsurface) { | ||
978 | struct sway_subsurface *subsurface = | ||
979 | calloc(1, sizeof(struct sway_subsurface)); | ||
980 | if (subsurface == NULL) { | ||
981 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
982 | return; | ||
983 | } | ||
984 | subsurface->child.parent = child; | ||
985 | wl_list_insert(&child->children, &subsurface->child.link); | ||
986 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
987 | wlr_subsurface->surface); | ||
988 | |||
989 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
990 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
991 | |||
992 | subsurface->child.mapped = true; | ||
993 | |||
994 | view_child_damage(&subsurface->child, true); | ||
995 | } | ||
996 | |||
997 | static bool view_child_is_mapped(struct sway_view_child *child) { | ||
998 | while (child) { | ||
999 | if (!child->mapped) { | ||
1000 | return false; | ||
1001 | } | ||
1002 | child = child->parent; | ||
1003 | } | ||
1004 | return true; | ||
1005 | } | ||
1006 | |||
1007 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
1008 | if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { | ||
1009 | return; | ||
1010 | } | ||
1011 | int sx, sy; | ||
1012 | child->impl->get_view_coords(child, &sx, &sy); | ||
1013 | desktop_damage_surface(child->surface, | ||
1014 | child->view->container->pending.content_x - | ||
1015 | child->view->geometry.x + sx, | ||
1016 | child->view->container->pending.content_y - | ||
1017 | child->view->geometry.y + sy, whole); | ||
1018 | } | ||
1019 | |||
1020 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
1021 | void *data) { | ||
1022 | struct sway_view_child *child = | ||
1023 | wl_container_of(listener, child, surface_commit); | ||
1024 | view_child_damage(child, false); | ||
1025 | } | ||
1026 | |||
1027 | static void view_child_handle_surface_new_subsurface( | ||
1028 | struct wl_listener *listener, void *data) { | ||
1029 | struct sway_view_child *child = | ||
1030 | wl_container_of(listener, child, surface_new_subsurface); | ||
1031 | struct wlr_subsurface *subsurface = data; | ||
1032 | view_child_subsurface_create(child, subsurface); | ||
1033 | } | ||
1034 | |||
1035 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
1036 | void *data) { | ||
1037 | struct sway_view_child *child = | ||
1038 | wl_container_of(listener, child, surface_destroy); | ||
1039 | view_child_destroy(child); | ||
1040 | } | ||
1041 | |||
1042 | static void view_init_subsurfaces(struct sway_view *view, | ||
1043 | struct wlr_surface *surface) { | ||
1044 | struct wlr_subsurface *subsurface; | ||
1045 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1046 | current.link) { | ||
1047 | view_subsurface_create(view, subsurface); | ||
1048 | } | ||
1049 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1050 | current.link) { | ||
1051 | view_subsurface_create(view, subsurface); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
1056 | struct wlr_surface *surface) { | ||
1057 | struct wlr_subsurface *subsurface; | ||
1058 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1059 | current.link) { | ||
1060 | view_child_subsurface_create(view_child, subsurface); | ||
1061 | } | ||
1062 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1063 | current.link) { | ||
1064 | view_child_subsurface_create(view_child, subsurface); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1069 | void *data) { | ||
1070 | struct sway_view_child *child = | ||
1071 | wl_container_of(listener, child, surface_map); | ||
1072 | child->mapped = true; | ||
1073 | view_child_damage(child, true); | ||
1074 | } | ||
1075 | |||
1076 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1077 | void *data) { | ||
1078 | struct sway_view_child *child = | ||
1079 | wl_container_of(listener, child, surface_unmap); | ||
1080 | view_child_damage(child, true); | ||
1081 | child->mapped = false; | ||
1082 | } | ||
1083 | |||
1084 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1085 | void *data) { | ||
1086 | struct sway_view_child *child = | ||
1087 | wl_container_of(listener, child, view_unmap); | ||
1088 | view_child_damage(child, true); | ||
1089 | child->mapped = false; | ||
1090 | } | ||
1091 | |||
1092 | void view_child_init(struct sway_view_child *child, | ||
1093 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1094 | struct wlr_surface *surface) { | ||
1095 | child->impl = impl; | ||
1096 | child->view = view; | ||
1097 | child->surface = surface; | ||
1098 | wl_list_init(&child->children); | ||
1099 | |||
1100 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1101 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1102 | wl_signal_add(&surface->events.new_subsurface, | ||
1103 | &child->surface_new_subsurface); | ||
1104 | child->surface_new_subsurface.notify = | ||
1105 | view_child_handle_surface_new_subsurface; | ||
1106 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1107 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1108 | |||
1109 | // Not all child views have a map/unmap event | ||
1110 | child->surface_map.notify = view_child_handle_surface_map; | ||
1111 | wl_list_init(&child->surface_map.link); | ||
1112 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1113 | wl_list_init(&child->surface_unmap.link); | ||
1114 | |||
1115 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1116 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1117 | |||
1118 | struct sway_container *container = child->view->container; | ||
1119 | if (container != NULL) { | ||
1120 | struct sway_workspace *workspace = container->pending.workspace; | ||
1121 | if (workspace) { | ||
1122 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1126 | view_child_init_subsurfaces(child, surface); | ||
1127 | } | ||
1128 | |||
1129 | void view_child_destroy(struct sway_view_child *child) { | ||
1130 | if (view_child_is_mapped(child) && child->view->container != NULL) { | ||
1131 | view_child_damage(child, true); | ||
1132 | } | ||
1133 | |||
1134 | if (child->parent != NULL) { | ||
1135 | wl_list_remove(&child->link); | ||
1136 | child->parent = NULL; | ||
1137 | } | ||
1138 | |||
1139 | struct sway_view_child *subchild, *tmpchild; | ||
1140 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | ||
1141 | wl_list_remove(&subchild->link); | ||
1142 | subchild->parent = NULL; | ||
1143 | // The subchild lost its parent link, so it cannot see that the parent | ||
1144 | // is unmapped. Unmap it directly. | ||
1145 | subchild->mapped = false; | ||
1146 | } | ||
1147 | |||
1148 | wl_list_remove(&child->surface_commit.link); | ||
1149 | wl_list_remove(&child->surface_destroy.link); | ||
1150 | wl_list_remove(&child->surface_map.link); | ||
1151 | wl_list_remove(&child->surface_unmap.link); | ||
1152 | wl_list_remove(&child->view_unmap.link); | ||
1153 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1154 | |||
1155 | if (child->impl && child->impl->destroy) { | ||
1156 | child->impl->destroy(child); | ||
1157 | } else { | ||
1158 | free(child); | ||
1159 | } | 949 | } |
1160 | } | 950 | } |
1161 | 951 | ||
1162 | 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) { |
1163 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 953 | struct wlr_xdg_surface *xdg_surface; |
1164 | struct wlr_xdg_surface *xdg_surface = | 954 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1165 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1166 | if (xdg_surface == NULL) { | ||
1167 | return NULL; | ||
1168 | } | ||
1169 | return view_from_wlr_xdg_surface(xdg_surface); | 955 | return view_from_wlr_xdg_surface(xdg_surface); |
1170 | } | 956 | } |
1171 | #if HAVE_XWAYLAND | 957 | #if HAVE_XWAYLAND |
1172 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 958 | struct wlr_xwayland_surface *xsurface; |
1173 | struct wlr_xwayland_surface *xsurface = | 959 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1174 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1175 | if (xsurface == NULL) { | ||
1176 | return NULL; | ||
1177 | } | ||
1178 | return view_from_wlr_xwayland_surface(xsurface); | 960 | return view_from_wlr_xwayland_surface(xsurface); |
1179 | } | 961 | } |
1180 | #endif | 962 | #endif |
1181 | if (wlr_surface_is_subsurface(wlr_surface)) { | 963 | struct wlr_subsurface *subsurface; |
1182 | struct wlr_subsurface *subsurface = | 964 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1183 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1184 | if (subsurface == NULL) { | ||
1185 | return NULL; | ||
1186 | } | ||
1187 | return view_from_wlr_surface(subsurface->parent); | 965 | return view_from_wlr_surface(subsurface->parent); |
1188 | } | 966 | } |
1189 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 967 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1190 | return NULL; | 968 | return NULL; |
1191 | } | 969 | } |
1192 | 970 | ||
@@ -1267,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1267 | return len; | 1045 | return len; |
1268 | } | 1046 | } |
1269 | 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 | |||
1270 | void view_update_title(struct sway_view *view, bool force) { | 1060 | void view_update_title(struct sway_view *view, bool force) { |
1271 | const char *title = view_get_title(view); | 1061 | const char *title = view_get_title(view); |
1272 | 1062 | ||
@@ -1282,29 +1072,41 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1282 | 1072 | ||
1283 | free(view->container->title); | 1073 | free(view->container->title); |
1284 | free(view->container->formatted_title); | 1074 | free(view->container->formatted_title); |
1285 | if (title) { | 1075 | |
1286 | size_t len = parse_title_format(view, NULL); | 1076 | size_t len = parse_title_format(view, NULL); |
1077 | |||
1078 | if (len) { | ||
1287 | char *buffer = calloc(len + 1, sizeof(char)); | 1079 | char *buffer = calloc(len + 1, sizeof(char)); |
1288 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1080 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1289 | return; | 1081 | return; |
1290 | } | 1082 | } |
1291 | parse_title_format(view, buffer); | ||
1292 | 1083 | ||
1293 | view->container->title = strdup(title); | 1084 | parse_title_format(view, buffer); |
1294 | view->container->formatted_title = buffer; | 1085 | view->container->formatted_title = buffer; |
1295 | } else { | 1086 | } else { |
1296 | view->container->title = NULL; | ||
1297 | view->container->formatted_title = NULL; | 1087 | view->container->formatted_title = NULL; |
1298 | } | 1088 | } |
1299 | 1089 | ||
1090 | view->container->title = title ? strdup(title) : NULL; | ||
1091 | |||
1300 | // Update title after the global font height is updated | 1092 | // Update title after the global font height is updated |
1301 | 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 | } | ||
1302 | 1100 | ||
1303 | ipc_event_window(view->container, "title"); | 1101 | ipc_event_window(view->container, "title"); |
1304 | 1102 | ||
1305 | if (view->foreign_toplevel && title) { | 1103 | if (view->foreign_toplevel && title) { |
1306 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1104 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1307 | } | 1105 | } |
1106 | |||
1107 | if (view->ext_foreign_toplevel) { | ||
1108 | update_ext_foreign_toplevel(view); | ||
1109 | } | ||
1308 | } | 1110 | } |
1309 | 1111 | ||
1310 | bool view_is_visible(struct sway_view *view) { | 1112 | bool view_is_visible(struct sway_view *view) { |
@@ -1365,6 +1167,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1365 | return; | 1167 | return; |
1366 | } | 1168 | } |
1367 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1169 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1170 | container_update_itself_and_parents(view->container); | ||
1368 | } else { | 1171 | } else { |
1369 | view->urgent = (struct timespec){ 0 }; | 1172 | view->urgent = (struct timespec){ 0 }; |
1370 | if (view->urgent_timer) { | 1173 | if (view->urgent_timer) { |
@@ -1372,7 +1175,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1372 | view->urgent_timer = NULL; | 1175 | view->urgent_timer = NULL; |
1373 | } | 1176 | } |
1374 | } | 1177 | } |
1375 | container_damage_whole(view->container); | ||
1376 | 1178 | ||
1377 | ipc_event_window(view->container, "urgent"); | 1179 | ipc_event_window(view->container, "urgent"); |
1378 | 1180 | ||
@@ -1386,40 +1188,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1386 | } | 1188 | } |
1387 | 1189 | ||
1388 | void view_remove_saved_buffer(struct sway_view *view) { | 1190 | void view_remove_saved_buffer(struct sway_view *view) { |
1389 | 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")) { |
1390 | return; | 1192 | return; |
1391 | } | 1193 | } |
1392 | struct sway_saved_buffer *saved_buf, *tmp; | 1194 | |
1393 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1195 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1394 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1196 | view->saved_surface_tree = NULL; |
1395 | wl_list_remove(&saved_buf->link); | 1197 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1396 | free(saved_buf); | ||
1397 | } | ||
1398 | } | 1198 | } |
1399 | 1199 | ||
1400 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1200 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1401 | int sx, int sy, void *data) { | 1201 | int sx, int sy, void *data) { |
1402 | struct sway_view *view = data; | 1202 | struct wlr_scene_tree *tree = data; |
1403 | 1203 | ||
1404 | if (surface && wlr_surface_has_buffer(surface)) { | 1204 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1405 | wlr_buffer_lock(&surface->buffer->base); | 1205 | if (!sbuf) { |
1406 | 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"); |
1407 | saved_buffer->buffer = surface->buffer; | 1207 | return; |
1408 | saved_buffer->width = surface->current.width; | ||
1409 | saved_buffer->height = surface->current.height; | ||
1410 | saved_buffer->x = view->container->surface_x + sx; | ||
1411 | saved_buffer->y = view->container->surface_y + sy; | ||
1412 | saved_buffer->transform = surface->current.transform; | ||
1413 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1414 | wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); | ||
1415 | } | 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); | ||
1416 | } | 1217 | } |
1417 | 1218 | ||
1418 | void view_save_buffer(struct sway_view *view) { | 1219 | void view_save_buffer(struct sway_view *view) { |
1419 | 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")) { |
1420 | view_remove_saved_buffer(view); | 1221 | view_remove_saved_buffer(view); |
1421 | } | 1222 | } |
1422 | 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); | ||
1423 | } | 1239 | } |
1424 | 1240 | ||
1425 | bool view_is_transient_for(struct sway_view *child, | 1241 | bool view_is_transient_for(struct sway_view *child, |
@@ -1427,3 +1243,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1427 | return child->impl->is_transient_for && | 1243 | return child->impl->is_transient_for && |
1428 | child->impl->is_transient_for(child, ancestor); | 1244 | child->impl->is_transient_for(child, ancestor); |
1429 | } | 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 | } | ||