From 44004e2d28a3a3af5b84f20f859c58ed360ef50d Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 4 Mar 2023 22:41:35 +0100 Subject: [PATCH] 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();