git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitd6193facd999cfc7e824f6f50c6cef62d99ca5fe
parent466fbf9b10
authorPeter Kaplan <[email protected]>
date2025-12-21 14:41
river: implement ext-foreign-toplevel-list

 river/Server.zig |  5 +++++
 river/Window.zig | 30 ++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/river/Server.zig b/river/Server.zig
index 7ef7698..fa4a051 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -82,6 +82,8 @@ data_control_manager: *wlr.DataControlManagerV1,
 export_dmabuf_manager: *wlr.ExportDmabufManagerV1,
 screencopy_manager: *wlr.ScreencopyManagerV1,
 
+foreign_toplevel_list: *wlr.ExtForeignToplevelListV1,
+
 scene: Scene,
 input_manager: InputManager,
 om: OutputManager,
@@ -147,6 +149,8 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
         .export_dmabuf_manager = try wlr.ExportDmabufManagerV1.create(wl_server),
         .screencopy_manager = try wlr.ScreencopyManagerV1.create(wl_server),
 
+        .foreign_toplevel_list = try wlr.ExtForeignToplevelListV1.create(wl_server, 1),
+
         .config = try Config.init(),
 
         .scene = undefined,
@@ -325,6 +329,7 @@ fn blocklist(server: *Server, global: *const wl.Global) bool {
         global == server.layer_shell.wlr_shell.global or
         global == server.xkb_bindings.global or
         global == server.screencopy_manager.global or
+        global == server.foreign_toplevel_list.global or
         global == server.export_dmabuf_manager.global or
         global == server.data_control_manager.global or
         global == server.om.wlr_output_manager.global or
diff --git a/river/Window.zig b/river/Window.zig
index eb77c26..b116136 100644
--- a/river/Window.zig
+++ b/river/Window.zig
@@ -228,6 +228,8 @@ rendering_requested: struct {
 /// The currently rendered position/dimensions of the window in the scene graph
 box: wlr.Box = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
 
+foreign_toplevel_handle: ?*wlr.ExtForeignToplevelHandleV1 = null,
+
 pub fn create(impl: Impl) error{OutOfMemory}!*Window {
     assert(impl != .destroying);
 
@@ -922,6 +924,15 @@ pub fn map(window: *Window) !void {
     assert(window.impl != .destroying);
     assert(window.state == .initialized);
     window.state = .mapped;
+
+    if (wlr.ExtForeignToplevelHandleV1.create(server.foreign_toplevel_list, &.{
+        .title = window.getTitle(),
+        .app_id = window.getAppId(),
+    })) |handle| {
+        window.foreign_toplevel_handle = handle;
+    } else |_| {
+        log.err("failed to create ext foreign toplevel handle", .{});
+    }
 }
 
 /// Called by the impl when the surface will no longer be displayed
@@ -935,14 +946,33 @@ pub fn unmap(window: *Window) void {
     window.state = .closing;
 
     server.wm.dirtyWindowing();
+
+    if (window.foreign_toplevel_handle) |handle| {
+        handle.destroy();
+        window.foreign_toplevel_handle = null;
+    }
 }
 
 pub fn notifyTitle(window: *Window) void {
     window.wm_scheduled.dirty_title = true;
     server.wm.dirtyWindowing();
+
+    if (window.foreign_toplevel_handle) |handle| {
+        handle.updateState(&.{
+            .title = window.getTitle(),
+            .app_id = window.getAppId(),
+        });
+    }
 }
 
 pub fn notifyAppId(window: *Window) void {
     window.wm_scheduled.dirty_app_id = true;
     server.wm.dirtyWindowing();
+
+    if (window.foreign_toplevel_handle) |handle| {
+        handle.updateState(&.{
+            .title = window.getTitle(),
+            .app_id = window.getAppId(),
+        });
+    }
 }