commit 0d2b1235479134adf59940ca6d8dd58de7ccfbd1 from: Peter Kaplan date: Mon Mar 30 08:02:35 2026 UTC river: ext-foreign-toplevel-image-capture-source commit - fa1a1b01bda020cb10fee649d8c5f34b749289cd commit + 0d2b1235479134adf59940ca6d8dd58de7ccfbd1 blob - 5d0f06871a5a131915fdae0264cf28dd28031cbf blob + d6c8e420e337d750ddf45767c43548b31995b359 --- river/Server.zig +++ river/Server.zig @@ -46,6 +46,7 @@ const XdgDecoration = @import("XdgDecoration.zig"); const XdgToplevel = @import("XdgToplevel.zig"); const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig"); const XwaylandView = @import("XwaylandView.zig"); +const View = @import("View.zig"); const log = std.log.scoped(.server); @@ -93,6 +94,7 @@ screencopy_manager: *wlr.ScreencopyManagerV1, image_copy_capture_manager: *wlr.ExtImageCopyCaptureManagerV1, output_image_capture_source_manager: *wlr.ExtOutputImageCaptureSourceManagerV1, +foreign_toplevel_image_capture_source_manager: *wlr.ExtForeignToplevelImageCaptureSourceManagerV1, foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1, @@ -127,6 +129,8 @@ request_activate: wl.Listener(*wlr.XdgActivationV1.eve wl.Listener(*wlr.XdgActivationV1.event.RequestActivate).init(handleRequestActivate), request_set_cursor_shape: wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape) = wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape).init(handleRequestSetCursorShape), +new_foreign_toplevel_capture_request: wl.Listener(*wlr.ExtForeignToplevelImageCaptureSourceManagerV1.Request) = + wl.Listener(*wlr.ExtForeignToplevelImageCaptureSourceManagerV1.Request).init(handleNewForeignToplevelCaptureRequest), pub fn init(server: *Server, runtime_xwayland: bool) !void { // We intentionally don't try to prevent memory leaks on error in this function @@ -181,6 +185,7 @@ pub fn init(server: *Server, runtime_xwayland: bool) ! .image_copy_capture_manager = try wlr.ExtImageCopyCaptureManagerV1.create(wl_server, 1), .output_image_capture_source_manager = try wlr.ExtOutputImageCaptureSourceManagerV1.create(wl_server, 1), + .foreign_toplevel_image_capture_source_manager = try wlr.ExtForeignToplevelImageCaptureSourceManagerV1.create(wl_server, 1), .foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server), @@ -392,6 +397,7 @@ fn blocklist(server: *Server, global: *const wl.Global global == server.screencopy_manager.global or global == server.image_copy_capture_manager.global or global == server.output_image_capture_source_manager.global or + global == server.foreign_toplevel_image_capture_source_manager.global or global == server.export_dmabuf_manager.global or global == server.data_control_manager.global or global == server.layout_manager.global or @@ -597,3 +603,24 @@ fn handleRequestSetCursorShape( } } } + +fn handleNewForeignToplevelCaptureRequest( + listener: *wl.Listener(*wlr.ExtForeignToplevelImageCaptureSourceManagerV1.Request), + request: *wlr.ExtForeignToplevelImageCaptureSourceManagerV1.Request, +) void { + const server: *Server = @fieldParentPtr("new_foreign_toplevel_capture_request", listener); + if (request.toplevel_handle.data) |opaque_view| { + const view: *View = @ptrCast(@alignCast(opaque_view)); + const capture_source = view.image_capture_source orelse wlr.ExtImageCaptureSourceV1.createWithSceneNode( + &view.image_capture_scene.tree.node, + server.wl_server.getEventLoop(), + server.allocator, + server.renderer, + ) catch { + log.err("failed to create ext image capture source", .{}); + return; + }; + + _ = request.accept(capture_source); + } +} blob - f3380a0bae03f8a0123832fd443b8c16891a0229 blob + 8cba7d0e9ab5371a213a477a38d87eb33754ae7b --- river/View.zig +++ river/View.zig @@ -140,6 +140,9 @@ saved_surface_tree: *wlr.SceneTree, borders: [4]*wlr.SceneRect, popup_tree: *wlr.SceneTree, +image_capture_scene: *wlr.Scene, +image_capture_source: ?*wlr.ExtImageCaptureSourceV1, + /// Bounds on the width/height of the view, set by the toplevel/xwayland_view implementation. constraints: Constraints = .{}, @@ -215,6 +218,9 @@ pub fn create(impl: Impl) error{OutOfMemory}!*View { }, .popup_tree = popup_tree, + .image_capture_scene = try wlr.Scene.create(), + .image_capture_source = null, + .pending_wm_stack_link = undefined, .pending_focus_stack_link = undefined, .inflight_wm_stack_link = undefined, @@ -230,6 +236,7 @@ pub fn create(impl: Impl) error{OutOfMemory}!*View { view.tree.node.setEnabled(false); view.popup_tree.node.setEnabled(false); view.saved_surface_tree.node.setEnabled(false); + view.image_capture_scene.restack_xwayland_surfaces = false; try SceneNodeData.attach(&view.tree.node, .{ .view = view }); try SceneNodeData.attach(&view.popup_tree.node, .{ .view = view }); @@ -250,6 +257,7 @@ pub fn destroy(view: *View, when: enum { lazy, assert // around until the current transaction completes. This function will be // called again in Root.commitTransaction() if (!view.saved_surface_tree.node.enabled) { + view.image_capture_scene.tree.node.destroy(); view.tree.node.destroy(); view.popup_tree.node.destroy(); @@ -665,6 +673,7 @@ pub fn map(view: *View) !void { .app_id = view.getAppId(), })) |handle| { view.ext_foreign_toplevel_handle = handle; + handle.data = view; } else |_| { log.err("failed to create ext foreign toplevel handle", .{}); } blob - 4fa775c12567c57d744b96a1fad5ddf3faf90738 blob + 02c0537e15b1c24b9cf313a3919061945d9fffcf --- river/XdgToplevel.zig +++ river/XdgToplevel.zig @@ -97,6 +97,7 @@ pub fn create(wlr_toplevel: *wlr.XdgToplevel) error{Ou errdefer toplevel.unmap.link.remove(); _ = try view.surface_tree.createSceneXdgSurface(wlr_toplevel.base); + _ = try view.image_capture_scene.tree.createSceneXdgSurface(wlr_toplevel.base); toplevel.view = view; blob - 836de81edddcbb8bb5c7a260519323a0658dc1c7 blob + 5224be4b194d184fd7e72a430345e29f5d72fbc2 --- river/XwaylandView.zig +++ river/XwaylandView.zig @@ -179,6 +179,12 @@ pub fn handleMap(listener: *wl.Listener(void)) void { return; }; + _ = view.image_capture_scene.tree.createSceneSurface(surface) catch { + log.err("out of memory", .{}); + surface.resource.getClient().postNoMemory(); + return; + }; + view.pending.box = .{ .x = 0, .y = 0,