From 1039e3a80035d2d71c50e7e06e40b25e44575bd4 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sat, 13 Dec 2025 00:30:42 -0500 Subject: [PATCH 1/5] still working on migrating to the new SceneNodeData --- src/Cursor.zig | 16 ++++--- src/LayerSurface.zig | 25 ++++++++--- src/Output.zig | 101 +++++++++++++++++++++++++++--------------- src/Root.zig | 38 ++++++++++++---- src/SceneNodeData.zig | 52 ++-------------------- src/Server.zig | 30 ++++++------- src/View.zig | 9 +++- 7 files changed, 148 insertions(+), 123 deletions(-) diff --git a/src/Cursor.zig b/src/Cursor.zig index 557f668..e9386af 100644 --- a/src/Cursor.zig +++ b/src/Cursor.zig @@ -86,8 +86,8 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { // Exit the switch if no focused output exists if (output == null) return; - const viewAtResult = output.?.viewAt(self.wlr_cursor.x, self.wlr_cursor.y); - if (viewAtResult == null) { + const surfaceAtResult = output.?.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y); + if (surfaceAtResult == null) { self.wlr_cursor.setXcursor(self.x_cursor_manager, "default"); server.seat.wlr_seat.pointerClearFocus(); @@ -98,10 +98,16 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { return; } - server.events.exec("ViewPointerMotion", .{viewAtResult.?.view.id, self.wlr_cursor.x, self.wlr_cursor.y}); + switch (surfaceAtResult.?.scene_node_data.*) { + .view => { + server.events.exec("ViewPointerMotion", .{surfaceAtResult.?.scene_node_data.view.id, self.wlr_cursor.x, self.wlr_cursor.y}); + }, + .layer_surface => { }, + else => unreachable + } - server.seat.wlr_seat.pointerNotifyEnter(viewAtResult.?.surface, viewAtResult.?.sx, viewAtResult.?.sy); - server.seat.wlr_seat.pointerNotifyMotion(time_msec, viewAtResult.?.sx, viewAtResult.?.sy); + server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy); + server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy); }, .move => { // TODO: Have these behave more like pointer motion if(self.drag.view) |view| { diff --git a/src/LayerSurface.zig b/src/LayerSurface.zig index 395d1f5..968cc74 100644 --- a/src/LayerSurface.zig +++ b/src/LayerSurface.zig @@ -6,12 +6,13 @@ const wlr = @import("wlroots"); const Utils = @import("Utils.zig"); const Output = @import("Output.zig"); -const SceneNodeData = @import("SceneNodeData.zig"); +const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; const gpa = std.heap.c_allocator; const server = &@import("main.zig").server; output: *Output, +scene_node_data: SceneNodeData, wlr_layer_surface: *wlr.LayerSurfaceV1, scene_layer_surface: *wlr.SceneLayerSurfaceV1, @@ -27,11 +28,26 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { const self = try gpa.create(LayerSurface); self.* = .{ - .output = @ptrCast(@alignCast(wlr_layer_surface.output.?.data)), + .output = blk: { + // These block things are dangerous + // There was no need for this + // But I cannot be stopped + // - Powerhungry programmer + const data = wlr_layer_surface.output.?.data; + if(data == null) unreachable; + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(wlr_layer_surface.output.?.data.?)); + break :blk switch(scene_node_data.*) { + .output => @fieldParentPtr("scene_node_data", scene_node_data), + else => unreachable + }; + }, .wlr_layer_surface = wlr_layer_surface, .scene_layer_surface = undefined, + .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), @@ -45,11 +61,6 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { }; } - try SceneNodeData.setData( - &self.scene_layer_surface.tree.node, - .{ .layer_surface = self }, - ); - self.wlr_layer_surface.surface.data = &self.scene_layer_surface.tree.node; self.wlr_layer_surface.events.destroy.add(&self.destroy); self.wlr_layer_surface.surface.events.map.add(&self.map); diff --git a/src/Output.zig b/src/Output.zig index 56498b2..43b9e42 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -5,11 +5,13 @@ const zwlr = @import("wayland").server.zwlr; const wlr = @import("wlroots"); const std = @import("std"); -const Server = @import("Server.zig"); const Utils = @import("Utils.zig"); + +const Server = @import("Server.zig"); const View = @import("View.zig"); const LayerSurface = @import("LayerSurface.zig"); -const SceneNodeData = @import("SceneNodeData.zig"); + +const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; const posix = std.posix; const gpa = std.heap.c_allocator; @@ -21,6 +23,7 @@ id: u64, wlr_output: *wlr.Output, state: wlr.Output.State, tree: *wlr.SceneTree, +scene_node_data: SceneNodeData, scene_output: *wlr.SceneOutput, layers: struct { @@ -59,6 +62,7 @@ pub fn init(wlr_output: *wlr.Output) ?*Output { .overlay = try self.tree.createSceneTree(), }, .scene_output = try server.root.scene.createSceneOutput(wlr_output), + .scene_node_data = SceneNodeData{ .output = self }, .state = wlr.Output.State.init() }; @@ -130,7 +134,6 @@ pub fn configureLayers(self: *Output) void { self.wlr_output.effectiveResolution(&output_box.width, &output_box.height); // Should calculate usable area here for LUA view positioning - for ([_]zwlr.LayerShellV1.Layer{ .background, .bottom, .top, .overlay }) |layer| { const tree = blk: { const trees = [_]*wlr.SceneTree{ @@ -146,43 +149,65 @@ pub fn configureLayers(self: *Output) void { while(it.next()) |node| { if(node.data == null) continue; - const layer_surface: *wlr.LayerSurfaceV1 = @ptrCast(@alignCast(node.data.?)); - _ = layer_surface.configure(@intCast(output_box.width), @intCast(output_box.height)); + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); + switch (scene_node_data.*) { + .layer_surface => { + _ = scene_node_data.layer_surface.wlr_layer_surface.configured( + @intCast(output_box.width), + @intCast(output_box.height) + ); + }, + else => { + std.log.err("Something other than a layer surface found in layer surface scene trees", .{}); + unreachable; + } + } } } } -const ViewAtResult = struct { - view: *View, +const SurfaceAtResult = struct { + scene_node_data: *SceneNodeData, surface: *wlr.Surface, sx: f64, sy: f64, }; -pub fn viewAt(self: *Output, lx: f64, ly: f64) ?ViewAtResult { +pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult { var sx: f64 = undefined; var sy: f64 = undefined; - if(self.layers.content.node.at(lx, ly, &sx, &sy)) |node| { - if (node.type != .buffer) return null; - const scene_buffer = wlr.SceneBuffer.fromNode(node); - const scene_surface = wlr.SceneSurface.tryFromBuffer(scene_buffer) orelse return null; + const layers = [_]*wlr.SceneTree{ + self.layers.top, + self.layers.overlay, + self.layers.fullscreen, + self.layers.content, + self.layers.bottom, + self.layers.background + }; - var it: ?*wlr.SceneTree = node.parent; + for(layers) |layer| { + if(layer.node.at(lx, ly, &sx, &sy)) |node| { + const scene_buffer = wlr.SceneBuffer.fromNode(node); + const scene_surface = wlr.SceneSurface.tryFromBuffer(scene_buffer) orelse continue; - while (it) |n| : (it = n.node.parent) { - if (n.node.data == null) continue; + if (node.data == null) continue; + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); - const view: *View = @ptrCast(@alignCast(n.node.data.?)); - - return ViewAtResult{ - .view = view, - .surface = scene_surface.surface, - .sx = sx, - .sy = sy, - }; + switch (scene_node_data.*) { + .layer_surface, .view => { + return SurfaceAtResult{ + .scene_node_data = scene_node_data, + .surface = scene_surface.surface, + .sx = sx, + .sy = sy, + }; + }, + else => continue + } } } + return null; } @@ -247,23 +272,27 @@ pub fn arrangeLayers(self: *Output) void { const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name); var it = layer.children.safeIterator(.forward); while (it.next()) |node| { - if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| { - const layer_surface = node_data.data.layer_surface; + if(node.data == null) continue; - if (!layer_surface.wlr_layer_surface.initialized) continue; + 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 + }; - // 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. + if (!layer_surface.wlr_layer_surface.initialized) continue; - layer_surface.scene_layer_surface.configure(&full_box, &full_box); + // 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: 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); - } + 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); } } } diff --git a/src/Root.zig b/src/Root.zig index 3614286..bca85ef 100644 --- a/src/Root.zig +++ b/src/Root.zig @@ -1,22 +1,25 @@ /// The root of Mezzaluna is, you guessed it, the root of many of the systems mez needs: -/// - Managing outputs -/// - + const Root = @This(); const std = @import("std"); const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); -const Output = @import("Output.zig"); -const View = @import("View.zig"); -const Utils = @import("Utils.zig"); - 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, @@ -48,8 +51,16 @@ pub fn deinit(self: *Root) void { while(it.next()) |node| { if(node.data == null) continue; - const view: *View = @ptrCast(@alignCast(node.data.?)); - view.deinit(); + 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(); @@ -62,7 +73,16 @@ pub fn viewById(self: *Root, id: u64) ?*View { while(output_it.next()) |o| { if(o.output.data == null) continue; - const output: *Output = @ptrCast(@alignCast(o.output.data.?)); + + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(o.output.data.?)); + const output: *Output = switch (scene_node_data.*) { + .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| { diff --git a/src/SceneNodeData.zig b/src/SceneNodeData.zig index 3ae95a7..6fadaa5 100644 --- a/src/SceneNodeData.zig +++ b/src/SceneNodeData.zig @@ -1,54 +1,10 @@ -const SceneNodeData = @This(); - -const std = @import("std"); -const wlr = @import("wlroots"); -const wl = @import("wayland").server.wl; - const View = @import("View.zig"); const LayerSurface = @import("LayerSurface.zig"); +const Output = @import("Output.zig"); -const gpa = std.heap.c_allocator; - -pub const Data = union(enum) { +const SceneNodeType = enum { view, layer_surface, output }; +pub const SceneNodeData = union(SceneNodeType) { view: *View, layer_surface: *LayerSurface, + output: *Output }; - -node: *wlr.SceneNode, -data: Data, -destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy), - -pub fn setData(node: *wlr.SceneNode, data: Data) !void { - const scene_node_data = try gpa.create(SceneNodeData); - - scene_node_data.* = .{ - .node = node, - .data = data, - }; - node.data = scene_node_data; - - node.events.destroy.add(&scene_node_data.destroy); -} - -fn handleDestroy(listener: *wl.Listener(void)) void { - const scene_node_data: *SceneNodeData = @fieldParentPtr("destroy", listener); - - scene_node_data.destroy.link.remove(); - scene_node_data.node.data = null; - - 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 e58f44e..3360f9e 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -4,19 +4,19 @@ const std = @import("std"); const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); -const Root = @import("Root.zig"); -const Seat = @import("Seat.zig"); -const Cursor = @import("Cursor.zig"); -const Keyboard = @import("Keyboard.zig"); -const LayerSurface = @import("LayerSurface.zig"); -const Output = @import("Output.zig"); -const View = @import("View.zig"); -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 RemoteLua = @import("RemoteLua.zig"); +const Root = @import("Root.zig"); +const Seat = @import("Seat.zig"); +const Cursor = @import("Cursor.zig"); +const Keyboard = @import("Keyboard.zig"); +const LayerSurface = @import("LayerSurface.zig"); +const Output = @import("Output.zig"); +const View = @import("View.zig"); +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 RemoteLua = @import("RemoteLua.zig"); const RemoteLuaManager = @import("RemoteLuaManager.zig"); const gpa = std.heap.c_allocator; @@ -35,15 +35,13 @@ xdg_shell: *wlr.XdgShell, layer_shell: *wlr.LayerShellV1, xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1, -// Input - allocator: *wlr.Allocator, root: Root, seat: Seat, cursor: Cursor, -// lua data +// Lua data keymaps: std.AutoHashMap(u64, Keymap), hooks: std.ArrayList(*Hook), events: Events, diff --git a/src/View.zig b/src/View.zig index ee46aaa..2787b2d 100644 --- a/src/View.zig +++ b/src/View.zig @@ -6,6 +6,8 @@ const wlr = @import("wlroots"); const Popup = @import("Popup.zig"); const Output = @import("Output.zig"); +const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; + const Utils = @import("Utils.zig"); const gpa = std.heap.c_allocator; @@ -20,6 +22,7 @@ output: ?*Output, xdg_toplevel: *wlr.XdgToplevel, xdg_toplevel_decoration: ?*wlr.XdgToplevelDecorationV1, scene_tree: *wlr.SceneTree, +scene_node_data: SceneNodeData, // Surface Listeners map: wl.Listener(void) = .init(handleMap), @@ -62,6 +65,8 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View { .xdg_toplevel = xdg_toplevel, .scene_tree = undefined, .xdg_toplevel_decoration = null, + + .scene_node_data = .{ .view = self } }; self.xdg_toplevel.base.surface.events.unmap.add(&self.unmap); @@ -76,8 +81,8 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View { self.scene_tree = try server.root.waiting_room.createSceneXdgSurface(xdg_toplevel.base); } - self.scene_tree.node.data = self; - self.xdg_toplevel.base.data = self.scene_tree; + self.scene_tree.node.data = &self.scene_node_data; + self.xdg_toplevel.base.data = &self.scene_node_data; self.xdg_toplevel.events.destroy.add(&self.destroy); self.xdg_toplevel.base.surface.events.map.add(&self.map); From 49a6ced28e50f9ae45b7bdbf7c3448b48dd32fb1 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sat, 13 Dec 2025 23:38:29 -0500 Subject: [PATCH 2/5] tiling is working again, and surface at is mostly in --- runtime/share/mezzaluna/base_config.lua | 1 - runtime/share/mezzaluna/init.lua | 1 + src/Cursor.zig | 2 +- src/Output.zig | 54 +++++++++++++++++-------- src/Root.zig | 14 +++++-- src/Server.zig | 7 ++-- src/View.zig | 1 + 7 files changed, 54 insertions(+), 26 deletions(-) diff --git a/runtime/share/mezzaluna/base_config.lua b/runtime/share/mezzaluna/base_config.lua index 30f845d..7c9b01d 100644 --- a/runtime/share/mezzaluna/base_config.lua +++ b/runtime/share/mezzaluna/base_config.lua @@ -282,4 +282,3 @@ function print_table(tbl, indent, seen) end end end - diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index 5487611..3168cc0 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -1,4 +1,5 @@ local env_conf = os.getenv("XDG_CONFIG_HOME") + if not env_conf then env_conf = os.getenv("HOME") if not env_conf then diff --git a/src/Cursor.zig b/src/Cursor.zig index e9386af..d193771 100644 --- a/src/Cursor.zig +++ b/src/Cursor.zig @@ -32,7 +32,7 @@ mode: enum { passthrough, move, resize } = .passthrough, drag: struct { start_x: c_int, start_y: c_int, - view: ?*View, + view: ?*View, view_offset_x: ?c_int, view_offset_y: ?c_int, }, diff --git a/src/Output.zig b/src/Output.zig index 43b9e42..6ea033a 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -92,7 +92,7 @@ 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; + wlr_output.data = &self.scene_node_data; server.events.exec("OutputInitPost", .{self.id}); @@ -187,24 +187,44 @@ pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult { }; for(layers) |layer| { - if(layer.node.at(lx, ly, &sx, &sy)) |node| { - const scene_buffer = wlr.SceneBuffer.fromNode(node); - const scene_surface = wlr.SceneSurface.tryFromBuffer(scene_buffer) orelse continue; + const node = layer.node.at(lx, ly, &sx, &sy); + if(node == null) continue; - if (node.data == null) continue; - const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?)); - - switch (scene_node_data.*) { - .layer_surface, .view => { - return SurfaceAtResult{ - .scene_node_data = scene_node_data, - .surface = scene_surface.surface, - .sx = sx, - .sy = sy, - }; - }, - else => continue + const surface: ?*wlr.Surface = blk: { + if (node.?.type == .buffer) { + const scene_buffer = wlr.SceneBuffer.fromNode(node.?); + if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| { + break :blk scene_surface.surface; + } } + break :blk null; + }; + if(surface == null) continue; + + const scene_node_data: *SceneNodeData = blk: { + var n = node.?; + while (true) { + if (@as(?*SceneNodeData, @ptrCast(@alignCast(n.data)))) |snd| { + break :blk snd; + } + if (n.parent) |parent_tree| { + n = &parent_tree.node; + } else { + continue; + } + } + }; + + switch (scene_node_data.*) { + .layer_surface, .view => { + return SurfaceAtResult{ + .scene_node_data = scene_node_data, + .surface = surface.?, + .sx = sx, + .sy = sy, + }; + }, + else => continue } } diff --git a/src/Root.zig b/src/Root.zig index bca85ef..63af0e1 100644 --- a/src/Root.zig +++ b/src/Root.zig @@ -74,8 +74,8 @@ pub fn viewById(self: *Root, id: u64) ?*View { while(output_it.next()) |o| { if(o.output.data == null) continue; - const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(o.output.data.?)); - const output: *Output = switch (scene_node_data.*) { + 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", .{}); @@ -88,8 +88,14 @@ pub fn viewById(self: *Root, id: u64) ?*View { while(node_it.next()) |node| { if(node.data == null) continue; - const view: *View = @as(*View, @ptrCast(@alignCast(node.data.?))); - if(view.id == id) return view; + 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; + } } } diff --git a/src/Server.zig b/src/Server.zig index 3360f9e..1c5b2bd 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -131,7 +131,7 @@ pub fn init(self: *Server) void { self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server)); // Add event listeners to events - // Backedn events + // Backend events self.backend.events.new_input.add(&self.new_input); self.backend.events.new_output.add(&self.new_output); @@ -211,12 +211,13 @@ fn handleNewXdgToplevelDecoration( decoration: *wlr.XdgToplevelDecorationV1 ) void { if(server.root.viewById(@intFromPtr(decoration.toplevel))) |view| { + std.log.debug("found view\n", .{}); view.xdg_toplevel_decoration = decoration; } } -fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void { - _ = xdg_popup; +fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), _: *wlr.XdgPopup) void { + std.log.debug("Unimplemented Server.handleNewXdgPopup\n", .{}); } fn handleNewLayerSurface( diff --git a/src/View.zig b/src/View.zig index 2787b2d..663ac5e 100644 --- a/src/View.zig +++ b/src/View.zig @@ -164,6 +164,7 @@ fn handleMap(listener: *wl.Listener(void)) void { &server.seat.keyboard_group.keyboard.modifiers ); + std.log.debug("setting view decoration mode to server side\n", .{}); if(view.xdg_toplevel_decoration) |decoration| { _ = decoration.setMode(wlr.XdgToplevelDecorationV1.Mode.server_side); } From 44f694f916a5e33973bfe02294eb12233e23ae1b Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sun, 14 Dec 2025 10:25:02 -0500 Subject: [PATCH 3/5] layer shells do not appear --- src/LayerSurface.zig | 12 +++++++++--- src/Server.zig | 2 +- src/View.zig | 1 - src/lua/View.zig | 4 ---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/LayerSurface.zig b/src/LayerSurface.zig index 968cc74..ee3e949 100644 --- a/src/LayerSurface.zig +++ b/src/LayerSurface.zig @@ -27,6 +27,7 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { const self = try gpa.create(LayerSurface); + self.* = .{ .output = blk: { // These block things are dangerous @@ -61,7 +62,6 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface { }; } - 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); @@ -104,8 +104,10 @@ fn handleDestroy( fn handleMap( listener: *wl.Listener(void) ) void { - const layer: *LayerSurface = @fieldParentPtr("map", listener); - layer.allowKeyboard(); + const layer_suraface: *LayerSurface = @fieldParentPtr("map", listener); + std.log.debug("layer surface mapped", .{}); + layer_suraface.output.arrangeLayers(); + layer_suraface.allowKeyboard(); } fn handleUnmap(listener: *wl.Listener(void)) void { @@ -113,6 +115,8 @@ fn handleUnmap(listener: *wl.Listener(void)) void { // FIXME: this crashes mez when killing mez layer_surface.output.arrangeLayers(); + + // TODO: Idk if this should be deiniting the layer surface entirely layer_surface.deinit(); } @@ -122,6 +126,8 @@ fn handleCommit( ) void { const layer_surface: *LayerSurface = @fieldParentPtr("commit", listener); + std.log.debug("layer surface commited", .{}); + if (!layer_surface.wlr_layer_surface.initial_commit) return; layer_surface.output.arrangeLayers(); } diff --git a/src/Server.zig b/src/Server.zig index 1c5b2bd..257e1f5 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -211,7 +211,6 @@ fn handleNewXdgToplevelDecoration( decoration: *wlr.XdgToplevelDecorationV1 ) void { if(server.root.viewById(@intFromPtr(decoration.toplevel))) |view| { - std.log.debug("found view\n", .{}); view.xdg_toplevel_decoration = decoration; } } @@ -224,6 +223,7 @@ fn handleNewLayerSurface( _: *wl.Listener(*wlr.LayerSurfaceV1), layer_surface: *wlr.LayerSurfaceV1 ) void { + std.log.debug("requested layer shell\n", .{}); if (layer_surface.output == null) { if (server.seat.focused_output == null) { std.log.err("No output available for new layer surface", .{}); diff --git a/src/View.zig b/src/View.zig index 663ac5e..2787b2d 100644 --- a/src/View.zig +++ b/src/View.zig @@ -164,7 +164,6 @@ fn handleMap(listener: *wl.Listener(void)) void { &server.seat.keyboard_group.keyboard.modifiers ); - std.log.debug("setting view decoration mode to server side\n", .{}); if(view.xdg_toplevel_decoration) |decoration| { _ = decoration.setMode(wlr.XdgToplevelDecorationV1.Mode.server_side); } diff --git a/src/lua/View.zig b/src/lua/View.zig index fc0bb40..c10248f 100644 --- a/src/lua/View.zig +++ b/src/lua/View.zig @@ -64,14 +64,10 @@ pub fn close(L: *zlua.Lua) i32 { // ---@param x number x position for view // ---@param y number y position for view pub fn set_position(L: *zlua.Lua) i32 { - std.log.debug("repositioning", .{}); - const view_id: u64 = @intCast(L.checkInteger(1)); const x: i32 = @intFromFloat(@round(L.checkNumber(2))); const y: i32 = @intFromFloat(@round(L.checkNumber(3))); - std.log.debug("position to set: ({d}, {d})", .{x, y}); - const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id); if(view) |v| { v.setPosition(x, y); From 42d66ea2b6ac8ea9cc20ff97ca46dcae98480fa1 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sun, 14 Dec 2025 11:33:44 -0500 Subject: [PATCH 4/5] 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 }; From 209cd8a540a4b87a3781e2702885f6c2950ef327 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sun, 14 Dec 2025 11:48:05 -0500 Subject: [PATCH 5/5] xdg_activation works --- ' | 222 ------------------------------------------------- src/Output.zig | 3 - src/Server.zig | 32 ++++--- 3 files changed, 20 insertions(+), 237 deletions(-) delete mode 100644 ' diff --git a/' b/' deleted file mode 100644 index 468b5f6..0000000 --- a/' +++ /dev/null @@ -1,222 +0,0 @@ -/// 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/Output.zig b/src/Output.zig index 2389bc0..f09f3af 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -305,9 +305,6 @@ fn handleDestroy( } pub fn arrangeLayers(self: *Output) void { - const debug = @import("Debug.zig"); - debug.debugPrintSceneTree(); - var full_box: wlr.Box = .{ .x = 0, .y = 0, diff --git a/src/Server.zig b/src/Server.zig index 257e1f5..a943678 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -11,13 +11,14 @@ const Keyboard = @import("Keyboard.zig"); const LayerSurface = @import("LayerSurface.zig"); const Output = @import("Output.zig"); const View = @import("View.zig"); -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 RemoteLua = @import("RemoteLua.zig"); const RemoteLuaManager = @import("RemoteLuaManager.zig"); +const Utils = @import("Utils.zig"); +const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData; const gpa = std.heap.c_allocator; const server = &@import("main.zig").server; @@ -34,6 +35,7 @@ shm: *wlr.Shm, xdg_shell: *wlr.XdgShell, layer_shell: *wlr.LayerShellV1, xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1, +xdg_activation: *wlr.XdgActivationV1, allocator: *wlr.Allocator, @@ -51,14 +53,11 @@ remote_lua_clients: std.DoublyLinkedList, new_input: wl.Listener(*wlr.InputDevice) = .init(handleNewInput), new_output: wl.Listener(*wlr.Output) = .init(handleNewOutput), // backend.events.destroy - -// XdgShell listeners new_xdg_toplevel: wl.Listener(*wlr.XdgToplevel) = .init(handleNewXdgToplevel), new_xdg_popup: wl.Listener(*wlr.XdgPopup) = .init(handleNewXdgPopup), new_xdg_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) = .init(handleNewXdgToplevelDecoration), - -// LayerShell Listeners new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) = .init(handleNewLayerSurface), +request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) = .init(handleRequestActivate), pub fn init(self: *Server) void { errdefer Utils.oomPanic(); @@ -92,6 +91,7 @@ pub fn init(self: *Server) void { .xdg_shell = try wlr.XdgShell.create(wl_server, 2), .layer_shell = try wlr.LayerShellV1.create(wl_server, 4), .xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(self.wl_server), + .xdg_activation = try wlr.XdgActivationV1.create(self.wl_server), .event_loop = event_loop, .session = session, .compositor = try wlr.Compositor.create(wl_server, 6, renderer), @@ -131,19 +131,13 @@ pub fn init(self: *Server) void { self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server)); // Add event listeners to events - // Backend events self.backend.events.new_input.add(&self.new_input); self.backend.events.new_output.add(&self.new_output); - - // XdgShell events self.xdg_shell.events.new_toplevel.add(&self.new_xdg_toplevel); self.xdg_shell.events.new_popup.add(&self.new_xdg_popup); - - // XdgDecorationManagerV1 events self.xdg_toplevel_decoration_manager.events.new_toplevel_decoration.add(&self.new_xdg_toplevel_decoration); - - // LayerShell events self.layer_shell.events.new_surface.add(&self.new_layer_surface); + self.xdg_activation.events.request_activate.add(&self.request_activate); self.events.exec("ServerStartPost", .{}); } @@ -236,3 +230,17 @@ fn handleNewLayerSurface( _ = LayerSurface.init(layer_surface); } + +fn handleRequestActivate( + _: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate), + event: *wlr.XdgActivationV1.event.RequestActivate, +) void { + if(event.surface.data == null) return; + + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(event.surface.data.?)); + if(scene_node_data.* == .view) { + scene_node_data.view.setFocused(); + } else { + std.log.warn("Ignoring request to activate non-view", .{}); + } +}