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); }