/// The root of Mezzaluna is, you guessed it, the root of many of the systems mez needs: const Root = @This(); const std = @import("std"); const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); const server = &@import("main.zig").server; const gpa = std.heap.c_allocator; const Output = @import("Output.zig"); const View = @import("View.zig"); const LayerSurface = @import("LayerSurface.zig"); const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; const Utils = @import("Utils.zig"); xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1, scene: *wlr.Scene, waiting_room: *wlr.SceneTree, scene_output_layout: *wlr.SceneOutputLayout, output_layout: *wlr.OutputLayout, pub fn init(self: *Root) void { std.log.info("Creating root of mezzaluna\n", .{}); errdefer Utils.oomPanic(); const output_layout = try wlr.OutputLayout.create(server.wl_server); errdefer output_layout.destroy(); const scene = try wlr.Scene.create(); errdefer scene.tree.node.destroy(); self.* = .{ .scene = scene, .waiting_room = try scene.tree.createSceneTree(), .output_layout = output_layout, .xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server), .scene_output_layout = try scene.attachOutputLayout(output_layout), }; } pub fn deinit(self: *Root) void { var it = self.scene.tree.children.iterator(.forward); while(it.next()) |node| { if(node.data == null) continue; const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); switch(scene_node_data.*) { .output => { scene_node_data.output.deinit(); }, else => { std.log.debug("The root has a child that is not an output", .{}); unreachable; } } } self.output_layout.destroy(); self.scene.tree.node.destroy(); } // Search output_layout's ouputs, and each outputs views pub fn viewById(self: *Root, id: u64) ?*View { var output_it = self.output_layout.outputs.iterator(.forward); while(output_it.next()) |o| { if(o.output.data == null) continue; const output_snd: *SceneNodeData = @ptrCast(@alignCast(o.output.data.?)); const output: *Output = switch (output_snd.*) { .output => |output_ptr| output_ptr, else => { std.log.err("Incorrect scene node type found", .{}); unreachable; } }; var node_it = output.layers.content.children.iterator(.forward); while(node_it.next()) |node| { if(node.data == null) continue; const view_snd: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); // TODO: Should we assert that we want only views to be here // -- Basically should we use switch statements for snd interactions // -- Or if statements, for simplicity if(view_snd.* == .view and view_snd.view.id == id) { return view_snd.view; } } } return null; } pub fn outputById(self: *Root, id: u64) ?*Output { var it = self.scene.outputs.iterator(.forward); while(it.next()) |scene_output| { if(scene_output.output.data == null) continue; const output: *Output = @as(*Output, @ptrCast(@alignCast(scene_output.output.data.?))); if(output.id == id) return output; } return null; } pub fn debugPrintSceneTree(self: *Root) void { std.log.debug("=== SCENE TREE DEBUG ===", .{}); printNode(&self.scene.tree.node, 0); std.log.debug("=== END SCENE TREE ===", .{}); } fn printNode(node: *wlr.SceneNode, depth: usize) void { errdefer Utils.oomPanic(); var indent = gpa.alloc(u8, depth); for(0..indent.len) |i| { } // Print node type and position const type_name = switch (node.type) { .tree => "TREE", .rect => "RECT", .buffer => "BUFFER", }; std.log.debug("{s}{s} @ ({d}, {d}) enabled={} visible={}", .{ indent, type_name, node.x, node.y, node.enabled, node.state.pending.enabled, }); // Print associated data if present if (node.data) |data| { const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(data)); switch (scene_node_data.*) { .output => |output| { std.log.debug("{s} → Output: {s} (focused={}, id={})", .{ indent, output.wlr_output.name, output.focused, output.id, }); }, .view => |view| { std.log.debug("{s} → View: id={} mapped={} focused={}", .{ indent, view.id, view.xdg_toplevel.base.surface.mapped, view.focused, }); if (view.xdg_toplevel.title) |title| { std.log.debug("{s} title=\"{s}\"", .{ indent, title }); } }, .layer_surface => |layer| { const layer_name = switch (layer.wlr_layer_surface.current.layer) { .background => "background", .bottom => "bottom", .top => "top", .overlay => "overlay", else => "unknown", }; std.log.debug("{s} → LayerSurface: layer={s} mapped={}", .{ indent, layer_name, layer.wlr_layer_surface.surface.mapped, }); if (layer.wlr_layer_surface.namespace.len > 0) { std.log.debug("{s} namespace=\"{s}\"", .{ indent, layer.wlr_layer_surface.namespace, }); } }, } } // Print buffer-specific info if (node.type == .buffer) { const scene_buffer = wlr.SceneBuffer.fromNode(node); std.log.debug("{s} buffer: {d}x{d}", .{ indent, scene_buffer.buffer.?.width, scene_buffer.buffer.?.height, }); // Check if it's a surface if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| { std.log.debug("{s} → Surface: {*}", .{ indent, scene_surface.surface }); } } // Recursively print children if this is a tree if (node.type == .tree) { const tree = wlr.SceneTree.fromNode(node); var it = tree.children.iterator(.forward); var child_count: usize = 0; while (it.next()) |child| { child_count += 1; printNode(child, depth + 1); } if (child_count == 0) { std.log.debug("{s} (no children)", .{indent}); } } }