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

commit6ac5fb8dc2f2faa8f15bbd921fd970128155df05
parent0a88539ced
authorIsaac Freund <[email protected]>
date2023-03-04 22:41
SceneNodeData: allow access from wlr_surfaces

This replaces the old View.fromWlrSurface function and is more general.

This commit also moves the xdg activation request_activate listener to
Server as it has no reason to be in View.

 river/IdleInhibitorManager.zig     | 30 ++++++++++++++++++++----------
 river/LayerSurface.zig             |  2 ++
 river/LockSurface.zig              |  2 ++
 river/Root.zig                     |  2 +-
 river/SceneNodeData.zig            | 11 ++++++++++-
 river/Server.zig                   | 35 +++++++++++++++++++++++++++++++++--
 river/View.zig                     | 34 ----------------------------------
 river/XdgPopup.zig                 |  2 +-
 river/XdgToplevel.zig              |  1 +
 river/XwaylandOverrideRedirect.zig |  8 +++++---
 river/XwaylandView.zig             |  6 +++++-
 11 files changed, 80 insertions(+), 53 deletions(-)

diff --git a/river/IdleInhibitorManager.zig b/river/IdleInhibitorManager.zig
index e3b198c..945f3f8 100644
--- a/river/IdleInhibitorManager.zig
+++ b/river/IdleInhibitorManager.zig
@@ -7,8 +7,9 @@ const wl = @import("wayland").server.wl;
 const server = &@import("main.zig").server;
 const util = @import("util.zig");
 
-const View = @import("View.zig");
 const IdleInhibitor = @import("IdleInhibitor.zig");
+const SceneNodeData = @import("SceneNodeData.zig");
+const View = @import("View.zig");
 
 idle_inhibit_manager: *wlr.IdleInhibitManagerV1,
 new_idle_inhibitor: wl.Listener(*wlr.IdleInhibitorV1),
@@ -32,17 +33,26 @@ pub fn idleInhibitCheckActive(self: *Self) void {
     var inhibited = false;
     var it = self.inhibitors.first;
     while (it) |node| : (it = node.next) {
-        if (View.fromWlrSurface(node.data.inhibitor.surface)) |v| {
-            // If view is visible,
-            if (v.current.output != null and v.current.tags & v.current.output.?.current.tags != 0) {
+        const node_data = SceneNodeData.fromSurface(node.data.inhibitor.surface) orelse continue;
+        switch (node_data.data) {
+            .view => |view| {
+                if (view.current.output != null and
+                    view.current.tags & view.current.output.?.current.tags != 0)
+                {
+                    inhibited = true;
+                    break;
+                }
+            },
+            .layer_surface => |layer_surface| {
+                if (layer_surface.wlr_layer_surface.mapped) {
+                    inhibited = true;
+                    break;
+                }
+            },
+            .lock_surface, .xwayland_override_redirect => {
                 inhibited = true;
                 break;
-            }
-        } else {
-            // If for whatever reason the inhibitor does not have a view, then
-            // assume it is visible.
-            inhibited = true;
-            break;
+            },
         }
     }
 
diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig
index 2a8a770..265a4dc 100644
--- a/river/LayerSurface.zig
+++ b/river/LayerSurface.zig
@@ -60,6 +60,8 @@ pub fn create(wlr_layer_surface: *wlr.LayerSurfaceV1) error{OutOfMemory}!void {
     try SceneNodeData.attach(&layer_surface.scene_layer_surface.tree.node, .{ .layer_surface = layer_surface });
     try SceneNodeData.attach(&layer_surface.popup_tree.node, .{ .layer_surface = layer_surface });
 
+    wlr_layer_surface.surface.data = @ptrToInt(&layer_surface.scene_layer_surface.tree.node);
+
     wlr_layer_surface.events.destroy.add(&layer_surface.destroy);
     wlr_layer_surface.events.map.add(&layer_surface.map);
     wlr_layer_surface.events.unmap.add(&layer_surface.unmap);
diff --git a/river/LockSurface.zig b/river/LockSurface.zig
index 83f95b3..8f58f45 100644
--- a/river/LockSurface.zig
+++ b/river/LockSurface.zig
@@ -51,6 +51,8 @@ pub fn create(wlr_lock_surface: *wlr.SessionLockSurfaceV1, lock: *wlr.SessionLoc
 
     try SceneNodeData.attach(&tree.node, .{ .lock_surface = lock_surface });
 
+    wlr_lock_surface.surface.data = @ptrToInt(&tree.node);
+
     wlr_lock_surface.output.events.mode.add(&lock_surface.output_mode);
     wlr_lock_surface.events.map.add(&lock_surface.map);
     wlr_lock_surface.events.destroy.add(&lock_surface.surface_destroy);
diff --git a/river/Root.zig b/river/Root.zig
index cbbfd21..419b392 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -195,7 +195,7 @@ pub fn at(self: Self, lx: f64, ly: f64) ?AtResult {
         break :blk null;
     };
 
-    if (SceneNodeData.get(node_at)) |scene_node_data| {
+    if (SceneNodeData.fromNode(node_at)) |scene_node_data| {
         return .{
             .surface = surface,
             .sx = sx,
diff --git a/river/SceneNodeData.zig b/river/SceneNodeData.zig
index f79bc59..782351f 100644
--- a/river/SceneNodeData.zig
+++ b/river/SceneNodeData.zig
@@ -50,7 +50,7 @@ pub fn attach(node: *wlr.SceneNode, data: Data) error{OutOfMemory}!void {
     node.events.destroy.add(&scene_node_data.destroy);
 }
 
-pub fn get(node: *wlr.SceneNode) ?*SceneNodeData {
+pub fn fromNode(node: *wlr.SceneNode) ?*SceneNodeData {
     var it: ?*wlr.SceneNode = node;
     while (it) |n| : (it = n.parent) {
         if (@intToPtr(?*SceneNodeData, n.data)) |scene_node_data| {
@@ -60,6 +60,15 @@ pub fn get(node: *wlr.SceneNode) ?*SceneNodeData {
     return null;
 }
 
+pub fn fromSurface(surface: *wlr.Surface) ?*SceneNodeData {
+    if (surface.getRootSurface()) |root_surface| {
+        if (@intToPtr(?*wlr.SceneNode, root_surface.data)) |node| {
+            return fromNode(node);
+        }
+    }
+    return null;
+}
+
 fn handleDestroy(listener: *wl.Listener(void)) void {
     const scene_node_data = @fieldParentPtr(SceneNodeData, "destroy", listener);
 
diff --git a/river/Server.zig b/river/Server.zig
index 291567e..3715f1e 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -27,17 +27,18 @@ const util = @import("util.zig");
 const Config = @import("Config.zig");
 const Control = @import("Control.zig");
 const DecorationManager = @import("DecorationManager.zig");
+const IdleInhibitorManager = @import("IdleInhibitorManager.zig");
 const InputManager = @import("InputManager.zig");
 const LayerSurface = @import("LayerSurface.zig");
 const LayoutManager = @import("LayoutManager.zig");
 const LockManager = @import("LockManager.zig");
 const Output = @import("Output.zig");
 const Root = @import("Root.zig");
+const SceneNodeData = @import("SceneNodeData.zig");
 const StatusManager = @import("StatusManager.zig");
 const XdgToplevel = @import("XdgToplevel.zig");
 const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
 const XwaylandView = @import("XwaylandView.zig");
-const IdleInhibitorManager = @import("IdleInhibitorManager.zig");
 
 const log = std.log.scoped(.server);
 
@@ -62,6 +63,7 @@ xwayland: if (build_options.xwayland) *wlr.Xwayland else void,
 new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void,
 
 xdg_activation: *wlr.XdgActivationV1,
+request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
 
 decoration_manager: DecorationManager,
 input_manager: InputManager,
@@ -115,6 +117,8 @@ pub fn init(self: *Self) !void {
     }
 
     self.xdg_activation = try wlr.XdgActivationV1.create(self.wl_server);
+    self.xdg_activation.events.request_activate.add(&self.request_activate);
+    self.request_activate.setNotify(handleRequestActivate);
 
     _ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server);
 
@@ -144,7 +148,14 @@ pub fn deinit(self: *Self) void {
     self.sigint_source.remove();
     self.sigterm_source.remove();
 
-    if (build_options.xwayland) self.xwayland.destroy();
+    self.new_xdg_surface.link.remove();
+    self.new_layer_surface.link.remove();
+    self.request_activate.link.remove();
+
+    if (build_options.xwayland) {
+        self.new_xwayland_surface.link.remove();
+        self.xwayland.destroy();
+    }
 
     self.wl_server.destroyClients();
 
@@ -251,3 +262,23 @@ fn handleNewXwaylandSurface(_: *wl.Listener(*wlr.XwaylandSurface), xwayland_surf
         };
     }
 }
+
+fn handleRequestActivate(
+    listener: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
+    event: *wlr.XdgActivationV1.event.RequestActivate,
+) void {
+    const server = @fieldParentPtr(Self, "request_activate", listener);
+
+    std.debug.print("made it here ig\n", .{});
+
+    const node_data = SceneNodeData.fromSurface(event.surface) orelse return;
+    switch (node_data.data) {
+        .view => |view| if (view.current.focus == 0) {
+            view.pending.urgent = true;
+            server.root.applyPending();
+        },
+        else => |tag| {
+            log.info("ignoring xdg-activation-v1 activate request of {s} surface", .{@tagName(tag)});
+        },
+    }
+}
diff --git a/river/View.zig b/river/View.zig
index 3c783a7..24e6652 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -147,9 +147,6 @@ float_box: wlr.Box = undefined,
 /// exiting fullscreen if there is no active layout.
 post_fullscreen_box: wlr.Box = undefined,
 
-request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) =
-    wl.Listener(*wlr.XdgActivationV1.event.RequestActivate).init(handleRequestActivate),
-
 pub fn create(impl: Impl) error{OutOfMemory}!*Self {
     const view = try util.gpa.create(Self);
     errdefer util.gpa.destroy(view);
@@ -383,21 +380,6 @@ pub fn applyConstraints(self: *Self, box: *wlr.Box) void {
     box.height = math.clamp(box.height, self.constraints.min_height, self.constraints.max_height);
 }
 
-/// Find and return the view corresponding to a given surface, if any
-pub fn fromWlrSurface(surface: *wlr.Surface) ?*Self {
-    if (surface.isXdgSurface()) {
-        const xdg_surface = wlr.XdgSurface.fromWlrSurface(surface) orelse return null;
-        if (xdg_surface.role == .toplevel) {
-            return @intToPtr(*Self, xdg_surface.data);
-        }
-    }
-    if (build_options.xwayland and surface.isXWaylandSurface()) {
-        const xwayland_surface = wlr.XwaylandSurface.fromWlrSurface(surface) orelse return null;
-        return @intToPtr(?*Self, xwayland_surface.data);
-    }
-    return null;
-}
-
 /// Called by the impl when the surface is ready to be displayed
 pub fn map(view: *Self) !void {
     log.debug("view '{?s}' mapped", .{view.getTitle()});
@@ -407,8 +389,6 @@ pub fn map(view: *Self) !void {
 
     view.pending.borders = !server.config.csdAllowed(view);
 
-    server.xdg_activation.events.request_activate.add(&view.request_activate);
-
     if (server.input_manager.defaultSeat().focused_output) |output| {
         // Center the initial pending box on the output
         view.pending.box.x = @divTrunc(math.max(0, output.usable_box.width - view.pending.box.width), 2);
@@ -444,8 +424,6 @@ pub fn unmap(view: *Self) void {
         server.root.hidden.pending.wm_stack.prepend(view);
     }
 
-    view.request_activate.link.remove();
-
     assert(view.mapped and !view.destroying);
     view.mapped = false;
 
@@ -468,15 +446,3 @@ pub fn notifyTitle(self: *const Self) void {
 pub fn notifyAppId(_: Self) void {
     // TODO reimplement foreign-toplevel-management I guess.
 }
-
-fn handleRequestActivate(
-    _: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
-    event: *wlr.XdgActivationV1.event.RequestActivate,
-) void {
-    if (fromWlrSurface(event.surface)) |view| {
-        if (view.current.focus == 0) {
-            view.pending.urgent = true;
-            server.root.applyPending();
-        }
-    }
-}
diff --git a/river/XdgPopup.zig b/river/XdgPopup.zig
index 2bb6d42..c539cc9 100644
--- a/river/XdgPopup.zig
+++ b/river/XdgPopup.zig
@@ -86,7 +86,7 @@ fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.Xdg
 fn handleReposition(listener: *wl.Listener(void)) void {
     const xdg_popup = @fieldParentPtr(XdgPopup, "reposition", listener);
 
-    const output = switch (SceneNodeData.get(&xdg_popup.root.node).?.data) {
+    const output = switch (SceneNodeData.fromNode(&xdg_popup.root.node).?.data) {
         .view => |view| view.current.output orelse return,
         .layer_surface => |layer_surface| layer_surface.output,
         else => unreachable,
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 64229ad..a3a96ac 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -79,6 +79,7 @@ pub fn create(xdg_toplevel: *wlr.XdgToplevel) error{OutOfMemory}!void {
     _ = try view.surface_tree.createSceneXdgSurface(xdg_toplevel.base);
 
     xdg_toplevel.base.data = @ptrToInt(view);
+    xdg_toplevel.base.surface.data = @ptrToInt(&view.tree.node);
 
     // Add listeners that are active over the view's entire lifetime
     const self = &view.impl.xdg_toplevel;
diff --git a/river/XwaylandOverrideRedirect.zig b/river/XwaylandOverrideRedirect.zig
index 8a8147b..e0a5f94 100644
--- a/river/XwaylandOverrideRedirect.zig
+++ b/river/XwaylandOverrideRedirect.zig
@@ -91,11 +91,12 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandS
 }
 
 fn mapImpl(self: *Self) error{OutOfMemory}!void {
-    self.surface_tree = try server.root.layers.xwayland_override_redirect.createSceneSubsurfaceTree(
-        self.xwayland_surface.surface.?,
-    );
+    const surface = self.xwayland_surface.surface.?;
+    self.surface_tree = try server.root.layers.xwayland_override_redirect.createSceneSubsurfaceTree(surface);
     try SceneNodeData.attach(&self.surface_tree.?.node, .{ .xwayland_override_redirect = self });
 
+    surface.data = @ptrToInt(&self.surface_tree.?.node);
+
     self.surface_tree.?.node.setPosition(self.xwayland_surface.x, self.xwayland_surface.y);
 
     self.xwayland_surface.events.set_geometry.add(&self.set_geometry);
@@ -130,6 +131,7 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSur
 
     self.set_geometry.link.remove();
 
+    self.xwayland_surface.surface.?.data = 0;
     self.surface_tree.?.node.destroy();
     self.surface_tree = null;
 
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index 3b1b83a..d071e60 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -159,8 +159,10 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface:
     const self = @fieldParentPtr(Self, "map", listener);
     const view = self.view;
 
-    // Add listeners that are only active while mapped
     const surface = xwayland_surface.surface.?;
+    surface.data = @ptrToInt(&view.tree.node);
+
+    // Add listeners that are only active while mapped
     xwayland_surface.events.set_title.add(&self.set_title);
     xwayland_surface.events.set_class.add(&self.set_class);
     xwayland_surface.events.request_fullscreen.add(&self.request_fullscreen);
@@ -206,6 +208,8 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface:
 fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "unmap", listener);
 
+    self.xwayland_surface.surface.?.data = 0;
+
     // Remove listeners that are only active while mapped
     self.set_title.link.remove();
     self.set_class.link.remove();