From 814daf0f2eba3a69a2298d09e50c7088fbee84ef Mon Sep 17 00:00:00 2001 From: Squibid Date: Thu, 4 Dec 2025 16:28:24 -0500 Subject: [PATCH] Create popups when a view recieves an event... This is currently incomplete and needs work to make sure that popups stay inside their parents current output. fixing popups, wip --- src/Popup.zig | 97 +++++++++++++++++++++++++++++++++++++++++++ src/SceneNodeData.zig | 14 +++++++ src/Server.zig | 8 ++-- src/View.zig | 12 ++++-- 4 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 src/Popup.zig diff --git a/src/Popup.zig b/src/Popup.zig new file mode 100644 index 0000000..930a2b1 --- /dev/null +++ b/src/Popup.zig @@ -0,0 +1,97 @@ +const Popup = @This(); + +const std = @import("std"); +const wl = @import("wayland").server.wl; +const wlr = @import("wlroots"); + +const Utils = @import("Utils.zig"); +const Output = @import("Output.zig"); +const SceneNodeData = @import("SceneNodeData.zig"); + +const gpa = std.heap.c_allocator; +const server = &@import("main.zig").server; + +id: u64, + +xdg_popup: *wlr.XdgPopup, +tree: *wlr.SceneTree, + +// Surface Listeners +destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy), +commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), +new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup), +reposition: wl.Listener(void) = wl.Listener(void).init(handleReposition), + +pub fn init( + xdg_popup: *wlr.XdgPopup, + parent: *wlr.SceneTree, +) *Popup { + errdefer Utils.oomPanic(); + + const self = try gpa.create(Popup); + errdefer gpa.destroy(self); + + self.* = .{ + .id = @intFromPtr(xdg_popup), + .xdg_popup = xdg_popup, + .tree = try parent.createSceneXdgSurface(xdg_popup.base), + }; + + xdg_popup.events.destroy.add(&self.destroy); + xdg_popup.base.surface.events.commit.add(&self.commit); + xdg_popup.base.events.new_popup.add(&self.new_popup); + xdg_popup.events.reposition.add(&self.reposition); + + return self; +} + +fn handleDestroy(listener: *wl.Listener(void)) void { + const popup: *Popup = @fieldParentPtr("destroy", listener); + + popup.destroy.link.remove(); + popup.commit.link.remove(); + popup.new_popup.link.remove(); + popup.reposition.link.remove(); + + gpa.destroy(popup); +} + +fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { + const popup: *Popup = @fieldParentPtr("commit", listener); + if (popup.xdg_popup.base.initial_commit) { + handleReposition(&popup.reposition); + } +} + +fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void { + const popup: *Popup = @fieldParentPtr("new_popup", listener); + + _ = Popup.init(xdg_popup, popup.tree); +} + +fn handleReposition(listener: *wl.Listener(void)) void { + const popup: *Popup = @fieldParentPtr("reposition", listener); + + var box: wlr.Box = undefined; + + // TODO: figure this out to prevent popups from rendering outside of the + // current monitor + // + // if (SceneNodeData.getFromNode(&popup.tree.node)) |node| { + // const output = switch (node.data) { + // .view => |view| view.output orelse return, + // .layer_surface => |layer_surface| layer_surface.output, + // }; + // + // server.root.output_layout.getBox(output.wlr_output, &box); + // } + + var root_lx: c_int = undefined; + var root_ly: c_int = undefined; + _ = popup.tree.node.coords(&root_lx, &root_ly); + + box.x -= root_lx; + box.y -= root_ly; + + popup.xdg_popup.unconstrainFromBox(&box); +} diff --git a/src/SceneNodeData.zig b/src/SceneNodeData.zig index 5a2c69d..3ae95a7 100644 --- a/src/SceneNodeData.zig +++ b/src/SceneNodeData.zig @@ -38,3 +38,17 @@ fn handleDestroy(listener: *wl.Listener(void)) void { gpa.destroy(scene_node_data); } + +pub fn getFromNode(node: *wlr.SceneNode) ?*SceneNodeData { + var n = node; + while (true) { + if (@as(?*SceneNodeData, @alignCast(@ptrCast(n.data)))) |scene_node_data| { + return scene_node_data; + } + if (n.parent) |parent_tree| { + n = &parent_tree.node; + } else { + return null; + } + } +} diff --git a/src/Server.zig b/src/Server.zig index 80d72ad..2886340 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -15,6 +15,7 @@ const Utils = @import("Utils.zig"); const Keymap = @import("types/Keymap.zig"); const Hook = @import("types/Hook.zig"); const Events = @import("types/Events.zig"); +const Popup = @import("Popup.zig"); const gpa = std.heap.c_allocator; const server = &@import("main.zig").server; @@ -199,11 +200,8 @@ fn handleNewXdgToplevelDecoration( } } -fn handleNewXdgPopup( - _: *wl.Listener(*wlr.XdgPopup), - _: *wlr.XdgPopup -) void { - std.log.err("Unimplemented handle new xdg popup", .{}); +fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void { + _ = xdg_popup; } fn handleNewLayerSurface( diff --git a/src/View.zig b/src/View.zig index 552d0a8..ee46aaa 100644 --- a/src/View.zig +++ b/src/View.zig @@ -4,6 +4,8 @@ const std = @import("std"); const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); +const Popup = @import("Popup.zig"); +const Output = @import("Output.zig"); const Utils = @import("Utils.zig"); const gpa = std.heap.c_allocator; @@ -14,6 +16,7 @@ focused: bool, id: u64, // workspace: Workspace, +output: ?*Output, xdg_toplevel: *wlr.XdgToplevel, xdg_toplevel_decoration: ?*wlr.XdgToplevelDecorationV1, scene_tree: *wlr.SceneTree, @@ -54,6 +57,7 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View { .focused = false, .mapped = false, .id = @intFromPtr(xdg_toplevel), + .output = null, .xdg_toplevel = xdg_toplevel, .scene_tree = undefined, @@ -66,6 +70,7 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View { // Later add to spesified output if(server.seat.focused_output) |output| { self.scene_tree = try output.layers.content.createSceneXdgSurface(xdg_toplevel.base); + self.output = output; } else { std.log.err("No output to attach new view to", .{}); self.scene_tree = try server.root.waiting_room.createSceneXdgSurface(xdg_toplevel.base); @@ -212,10 +217,9 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { } // --------- XdgToplevel Event Handlers --------- -fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), popup: *wlr.XdgPopup) void { - _ = listener; - _ = popup; - std.log.err("Unimplemented view handle new popup", .{}); +fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void { + const view: *View = @fieldParentPtr("new_popup", listener); + _ = Popup.init(xdg_popup, view.scene_tree); } fn handleRequestMove(