diff --git a/include/labwc.h b/include/labwc.h index 735f57b8..ff8030e8 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -188,6 +188,13 @@ struct server { struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; struct wl_listener xdg_toplevel_icon_set_icon; + struct { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager; + struct { + struct wl_listener new_request; + } on; + } toplevel_capture; + struct wl_list views; struct wl_list unmanaged_surfaces; diff --git a/include/view.h b/include/view.h index a8456de2..4f38302d 100644 --- a/include/view.h +++ b/include/view.h @@ -176,6 +176,11 @@ struct view { char *title; char *app_id; /* WM_CLASS for xwayland windows */ + struct { + struct wlr_scene *scene; + struct wlr_ext_image_capture_source_v1 *source; + } capture; + bool mapped; bool been_mapped; enum lab_ssd_mode ssd_mode; @@ -304,6 +309,7 @@ struct xdg_toplevel_view { /* Events unique to xdg-toplevel views */ struct wl_listener set_app_id; struct wl_listener request_show_window_menu; + struct wl_listener set_parent; struct wl_listener new_popup; }; diff --git a/src/foreign-toplevel/ext-foreign.c b/src/foreign-toplevel/ext-foreign.c index 748050fa..ab0ec3ed 100644 --- a/src/foreign-toplevel/ext-foreign.c +++ b/src/foreign-toplevel/ext-foreign.c @@ -75,6 +75,9 @@ ext_foreign_toplevel_init(struct ext_foreign_toplevel *ext_toplevel, return; } + /* In support for ext-toplevel-capture */ + ext_toplevel->handle->data = view; + /* Client side requests */ ext_toplevel->on.handle_destroy.notify = handle_handle_destroy; wl_signal_add(&ext_toplevel->handle->events.destroy, &ext_toplevel->on.handle_destroy); diff --git a/src/server.c b/src/server.c index bf424f9c..cc76bed0 100644 --- a/src/server.c +++ b/src/server.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L #include "config.h" +#include #include #include #include @@ -423,6 +424,25 @@ handle_renderer_lost(struct wl_listener *listener, void *data) wlr_renderer_destroy(old_renderer); } +static void +handle_toplevel_capture_request(struct wl_listener *listener, void *data) +{ + struct server *server = wl_container_of(listener, server, toplevel_capture.on.new_request); + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; + struct view *view = request->toplevel_handle->data; + assert(view); + wlr_log(WLR_INFO, "Got request to capture toplevel %s", view->app_id); + + if (!view->capture.source) { + view->capture.source = wlr_ext_image_capture_source_v1_create_with_scene_node( + &view->capture.scene->tree.node, server->wl_event_loop, + server->allocator, server->renderer); + } + assert(view->capture.source); + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( + request, view->capture.source); +} + void server_init(struct server *server) { @@ -656,6 +676,19 @@ server_init(struct server *server) wlr_screencopy_manager_v1_create(server->wl_display); wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); + + server->toplevel_capture.manager = + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create( + server->wl_display, 1); + if (server->toplevel_capture.manager) { + server->toplevel_capture.on.new_request.notify = handle_toplevel_capture_request; + wl_signal_add(&server->toplevel_capture.manager->events.new_request, + &server->toplevel_capture.on.new_request); + } else { + /* Allow safe removal on shutdown */ + wl_list_init(&server->toplevel_capture.on.new_request.link); + } + wlr_data_control_manager_v1_create(server->wl_display); wlr_ext_data_control_manager_v1_create(server->wl_display, LAB_EXT_DATA_CONTROL_VERSION); @@ -788,6 +821,8 @@ server_finish(struct server *server) server->drm_lease_request.notify = NULL; } + wl_list_remove(&server->toplevel_capture.on.new_request.link); + wlr_backend_destroy(server->backend); wlr_allocator_destroy(server->allocator); diff --git a/src/view.c b/src/view.c index 87ef8566..242c2ea5 100644 --- a/src/view.c +++ b/src/view.c @@ -2570,6 +2570,8 @@ view_init(struct view *view) view->title = xstrdup(""); view->app_id = xstrdup(""); + + view->capture.scene = wlr_scene_create(); } void @@ -2596,6 +2598,8 @@ view_destroy(struct view *view) zfree(view->title); zfree(view->app_id); + wlr_scene_node_destroy(&view->capture.scene->tree.node); + if (view->foreign_toplevel) { foreign_toplevel_destroy(view->foreign_toplevel); view->foreign_toplevel = NULL; diff --git a/src/xdg-popup.c b/src/xdg-popup.c index aded80b5..9bf7a64a 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -166,4 +166,6 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base); node_descriptor_create(wlr_popup->base->surface->data, LAB_NODE_XDG_POPUP, view, /*data*/ NULL); + + wlr_scene_xdg_surface_create(&view->capture.scene->tree, wlr_popup->base); } diff --git a/src/xdg.c b/src/xdg.c index 83ab9bb9..a3e8a7a0 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -28,6 +28,8 @@ #define LAB_XDG_SHELL_VERSION 6 #define CONFIGURE_TIMEOUT_MS 100 +static struct view *xdg_toplevel_view_get_root(struct view *view); + static struct xdg_toplevel_view * xdg_toplevel_view_from_view(struct view *view) { @@ -377,6 +379,7 @@ handle_destroy(struct wl_listener *listener, void *data) /* Remove xdg-shell view specific listeners */ wl_list_remove(&xdg_toplevel_view->set_app_id.link); wl_list_remove(&xdg_toplevel_view->request_show_window_menu.link); + wl_list_remove(&xdg_toplevel_view->set_parent.link); wl_list_remove(&xdg_toplevel_view->new_popup.link); wl_list_remove(&view->commit.link); @@ -488,6 +491,23 @@ handle_request_show_window_menu(struct wl_listener *listener, void *data) menu_open_root(menu, cursor->x, cursor->y); } +static void +handle_set_parent(struct wl_listener *listener, void *data) +{ + struct xdg_toplevel_view *xdg_toplevel_view = wl_container_of( + listener, xdg_toplevel_view, set_parent); + struct view *view = &xdg_toplevel_view->base; + struct view *view_root = xdg_toplevel_view_get_root(view); + if (view_root == view) { + return; + } + struct wlr_scene_node *node, *tmp; + wl_list_for_each_safe(node, tmp, &view->capture.scene->tree.children, link) { + wlr_log(WLR_INFO, "moving capture scene node to view_root"); + wlr_scene_node_reparent(node, &view_root->capture.scene->tree); + } +} + static void handle_set_title(struct wl_listener *listener, void *data) { @@ -1050,6 +1070,9 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) view_connect_map(view, xdg_surface->surface); + struct view *root_view = xdg_toplevel_view_get_root(view); + wlr_scene_xdg_surface_create(&root_view->capture.scene->tree, xdg_surface); + struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; CONNECT_SIGNAL(toplevel, view, destroy); CONNECT_SIGNAL(toplevel, view, request_move); @@ -1063,6 +1086,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) /* Events specific to XDG toplevel views */ CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_app_id); CONNECT_SIGNAL(toplevel, xdg_toplevel_view, request_show_window_menu); + CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_parent); CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup); wl_list_insert(&server->views, &view->link); diff --git a/src/xwayland.c b/src/xwayland.c index 6b408a78..bbff267b 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -844,6 +844,7 @@ xwayland_view_map(struct view *view) return; } view->content_tree = tree; + wlr_scene_subsurface_tree_create(&view->capture.scene->tree, view->surface); } /*