From 42d66ea2b6ac8ea9cc20ff97ca46dcae98480fa1 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sun, 14 Dec 2025 11:33:44 -0500 Subject: [PATCH] debug scene tree printer, layers are functional again --- ' | 222 ++++++++++++++++++++++++++++++++++++++++++ src/Debug.zig | 127 ++++++++++++++++++++++++ src/LayerSurface.zig | 5 +- src/Output.zig | 64 ++++++++---- src/Root.zig | 4 + src/SceneNodeData.zig | 16 ++- 6 files changed, 417 insertions(+), 21 deletions(-) create mode 100644 ' create mode 100644 src/Debug.zig diff --git a/' b/' new file mode 100644 index 0000000..468b5f6 --- /dev/null +++ b/' @@ -0,0 +1,222 @@ +/// 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}); + } + } +} diff --git a/src/Debug.zig b/src/Debug.zig new file mode 100644 index 0000000..426b5ac --- /dev/null +++ b/src/Debug.zig @@ -0,0 +1,127 @@ +const Debug = @This(); + +const std = @import("std"); +const wlr = @import("wlroots"); + +const server = &@import("main.zig").server; +const gpa = std.heap.c_allocator; + +const Utils = @import("Utils.zig"); +const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; + +pub fn debugPrintSceneTree() void { + std.log.debug("=== SCENE TREE DEBUG ===", .{}); + printNode(&server.root.scene.tree.node, 0); + std.log.debug("=== END SCENE TREE ===", .{}); +} + +fn printNode(node: *wlr.SceneNode, depth: usize) void { + errdefer Utils.oomPanic(); + + var buffer: std.ArrayList(u8) = try .initCapacity(gpa, 512); + defer buffer.deinit(gpa); + const writer = buffer.writer(gpa); + + // Add indentation + for (0..depth) |_| { + writer.writeAll(" ") catch unreachable; + } + + // Print node type and position + const type_name = switch (node.type) { + .tree => "TREE", + .rect => "RECT", + .buffer => "BUFFER" + }; + + writer.print("{s} @ ({d}, {d}) enabled={}", .{ + type_name, + node.x, + node.y, + node.enabled + }) catch unreachable; + + // Add associated data if present + if (node.data) |data| { + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(data)); + switch (scene_node_data.*) { + .root => { + writer.print(" → Root Scene Tree", .{}) catch unreachable; + }, + .output => |output| { + writer.print(" → Output: {s} (focused={}, id={})", .{ + output.wlr_output.name, + output.focused, + output.id, + }) catch unreachable; + }, + .output_layer => { + writer.print(" → Output Layer", .{}) catch unreachable; + }, + .view => |view| { + writer.print(" → View: id={} mapped={} focused={}", .{ + view.id, + view.xdg_toplevel.base.surface.mapped, + view.focused, + }) catch unreachable; + if (view.xdg_toplevel.title) |title| { + writer.print(" title=\"{s}\"", .{title}) catch unreachable; + } + }, + .layer_surface => |layer| { + const layer_name = switch (layer.wlr_layer_surface.current.layer) { + .background => "background", + .bottom => "bottom", + .top => "top", + .overlay => "overlay", + else => "unknown", + }; + writer.print(" → LayerSurface: layer={s} mapped={}", .{ + layer_name, + layer.wlr_layer_surface.surface.mapped, + }) catch unreachable; + const namespace = std.mem.span(layer.wlr_layer_surface.namespace); + if (namespace.len > 0) { + writer.print(" namespace=\"{s}\"", .{namespace}) catch unreachable; + } + } + } + } + + // Add buffer-specific info + if (node.type == .buffer) { + const scene_buffer = wlr.SceneBuffer.fromNode(node); + writer.print(" buffer: {d}x{d}", .{ + if (scene_buffer.buffer == null) -1 else scene_buffer.buffer.?.width, + if (scene_buffer.buffer == null) -1 else scene_buffer.buffer.?.height, + }) catch unreachable; + + // Check if it's a surface + if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| { + writer.print(" → Surface: {*}", .{scene_surface.surface}) catch unreachable; + } + } + + // Print the complete line + std.log.debug("{s}", .{buffer.items}); + + // 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) { + var empty_buffer: std.ArrayList(u8) = try .initCapacity(gpa, 512); + defer empty_buffer.deinit(gpa); + for (0..depth) |_| { + empty_buffer.writer(gpa).writeAll("\t") catch unreachable; + } + empty_buffer.writer(gpa).writeAll(" (no children)") catch unreachable; + std.log.debug("{s}", .{empty_buffer.items}); + } + } +} diff --git a/src/LayerSurface.zig b/src/LayerSurface.zig index ee3e949..b6defaf 100644 --- a/src/LayerSurface.zig +++ b/src/LayerSurface.zig @@ -47,8 +47,6 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { .scene_node_data = .{ .layer_surface = self } }; - self.wlr_layer_surface.surface.data = &self.scene_node_data; - if(server.seat.focused_output) |output| { self.scene_layer_surface = switch (wlr_layer_surface.current.layer) { .background => try output.layers.background.createSceneLayerSurfaceV1(wlr_layer_surface), @@ -62,6 +60,9 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { }; } + self.wlr_layer_surface.surface.data = &self.scene_node_data; + self.scene_layer_surface.tree.node.data = &self.scene_node_data; + self.wlr_layer_surface.events.destroy.add(&self.destroy); self.wlr_layer_surface.surface.events.map.add(&self.map); self.wlr_layer_surface.surface.events.unmap.add(&self.unmap); diff --git a/src/Output.zig b/src/Output.zig index 6ea033a..2389bc0 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -35,6 +35,15 @@ layers: struct { overlay: *wlr.SceneTree }, +layer_scene_node_data: struct { + background: SceneNodeData, + bottom: SceneNodeData, + content: SceneNodeData, + top: SceneNodeData, + fullscreen: SceneNodeData, + overlay: SceneNodeData +}, + frame: wl.Listener(*wlr.Output) = .init(handleFrame), request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState), destroy: wl.Listener(*wlr.Output) = .init(handleDestroy), @@ -61,6 +70,14 @@ pub fn init(wlr_output: *wlr.Output) ?*Output { .fullscreen = try self.tree.createSceneTree(), .overlay = try self.tree.createSceneTree(), }, + .layer_scene_node_data = .{ + .background = .{ .output_layer = self.layers.background }, + .bottom = .{ .output_layer = self.layers.bottom }, + .content = .{ .output_layer = self.layers.content }, + .top = .{ .output_layer = self.layers.top }, + .fullscreen = .{ .output_layer = self.layers.fullscreen }, + .overlay = .{ .output_layer = self.layers.overlay } + }, .scene_output = try server.root.scene.createSceneOutput(wlr_output), .scene_node_data = SceneNodeData{ .output = self }, .state = wlr.Output.State.init() @@ -92,7 +109,15 @@ pub fn init(wlr_output: *wlr.Output) ?*Output { server.root.scene_output_layout.addOutput(layout_output, self.scene_output); self.setFocused(); - wlr_output.data = &self.scene_node_data; + self.wlr_output.data = &self.scene_node_data; + self.tree.node.data = &self.scene_node_data; + + self.layers.background.node.data = &self.layer_scene_node_data.background; + self.layers.bottom.node.data = &self.layer_scene_node_data.bottom; + self.layers.content.node.data = &self.layer_scene_node_data.content; + self.layers.top.node.data = &self.layer_scene_node_data.top; + self.layers.fullscreen.node.data = &self.layer_scene_node_data.fullscreen; + self.layers.overlay.node.data = &self.layer_scene_node_data.overlay; server.events.exec("OutputInitPost", .{self.id}); @@ -280,6 +305,9 @@ fn handleDestroy( } pub fn arrangeLayers(self: *Output) void { + const debug = @import("Debug.zig"); + debug.debugPrintSceneTree(); + var full_box: wlr.Box = .{ .x = 0, .y = 0, @@ -291,28 +319,30 @@ pub fn arrangeLayers(self: *Output) void { inline for (@typeInfo(zwlr.LayerShellV1.Layer).@"enum".fields) |comptime_layer| { const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name); var it = layer.children.safeIterator(.forward); + while (it.next()) |node| { - if(node.data == null) continue; + if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| { + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); - const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); - const layer_surface: *LayerSurface = switch(scene_node_data.*) { - .layer_surface => @fieldParentPtr("scene_node_data", scene_node_data), - else => continue - }; + const layer_surface: *LayerSurface = switch(node_data.*) { + .layer_surface => @fieldParentPtr("scene_node_data", scene_node_data), + else => continue + }; - if (!layer_surface.wlr_layer_surface.initialized) continue; + if (!layer_surface.wlr_layer_surface.initialized) continue; - // TEST: river seems to try and prevent clients from taking an - // exclusive size greater than half the screen by killing them. Do we - // need to? Clients can do quite a bit of nasty stuff and taking - // exclusive focus isn't even that bad. + // TEST: river seems to try and prevent clients from taking an + // exclusive size greater than half the screen by killing them. Do we + // need to? Clients can do quite a bit of nasty stuff and taking + // exclusive focus isn't even that bad. - layer_surface.scene_layer_surface.configure(&full_box, &full_box); + layer_surface.scene_layer_surface.configure(&full_box, &full_box); - // TEST: are these calls useless? - // const x = layer_surface.scene_layer_surface.tree.node.x; - // const y = layer_surface.scene_layer_surface.tree.node.y; - // layer_surface.scene_layer_surface.tree.node.setPosition(x, y); + // TEST: are these calls useless? + // const x = layer_surface.scene_layer_surface.tree.node.x; + // const y = layer_surface.scene_layer_surface.tree.node.y; + // layer_surface.scene_layer_surface.tree.node.setPosition(x, y); + } } } } diff --git a/src/Root.zig b/src/Root.zig index 63af0e1..d043580 100644 --- a/src/Root.zig +++ b/src/Root.zig @@ -19,6 +19,7 @@ const Utils = @import("Utils.zig"); xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1, scene: *wlr.Scene, +scene_node_data: SceneNodeData, waiting_room: *wlr.SceneTree, scene_output_layout: *wlr.SceneOutputLayout, @@ -38,11 +39,14 @@ pub fn init(self: *Root) void { self.* = .{ .scene = scene, + .scene_node_data = .{ .root = self }, .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), }; + + self.scene.tree.node.data = &self.scene_node_data; } pub fn deinit(self: *Root) void { diff --git a/src/SceneNodeData.zig b/src/SceneNodeData.zig index 6fadaa5..764bdd5 100644 --- a/src/SceneNodeData.zig +++ b/src/SceneNodeData.zig @@ -1,10 +1,22 @@ +const wlr = @import("wlroots"); + const View = @import("View.zig"); const LayerSurface = @import("LayerSurface.zig"); const Output = @import("Output.zig"); +const Root = @import("Root.zig"); + +const SceneNodeType = enum { + view, + layer_surface, + output, + output_layer, + root +}; -const SceneNodeType = enum { view, layer_surface, output }; pub const SceneNodeData = union(SceneNodeType) { view: *View, layer_surface: *LayerSurface, - output: *Output + output: *Output, + output_layer: *wlr.SceneTree, + root: *Root };