From 2c130539f66db6b87804f42919bffe03516c8c0a Mon Sep 17 00:00:00 2001 From: Squibid Date: Sat, 22 Nov 2025 18:16:43 -0500 Subject: [PATCH 1/9] intial setup for passing arbitrary data to hooks --- runtime/share/mezzaluna/init.lua | 37 ++++++++++++++++---------------- src/types/events.zig | 4 ++-- src/types/hook.zig | 21 ++++++++++++++++-- src/view.zig | 4 ++-- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index f55ff0b..08f0065 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -1,10 +1,10 @@ local env_conf = os.getenv("XDG_CONFIG_HOME") if not env_conf then - env_conf = os.getenv("HOME") - if not env_conf then - error("Couldn't determine potential config directory is $HOME set?") - end - env_conf = mez.fs.joinpath(env_conf, ".config") + env_conf = os.getenv("HOME") + if not env_conf then + error("Couldn't determine potential config directory is $HOME set?") + end + env_conf = mez.fs.joinpath(env_conf, ".config") end mez.path.config = mez.fs.joinpath(env_conf, "mez", "init.lua") @@ -12,27 +12,27 @@ package.path = package.path..";"..mez.fs.joinpath(env_conf, "mez", "lua", "?.lua -- this is an example mez.input.add_keymap("alt", "a", { - press = function() - print("hello from my keymap") - end + press = function() + print("hello from my keymap") + end }) mez.input.add_keymap("alt", "Return", { - press = function() - mez.api.spawn("foot") - end, + press = function() + mez.api.spawn("foot") + end, }) mez.input.add_keymap("alt", "c", { - press = function () - mez.api.close() - end + press = function () + mez.api.close() + end }) mez.input.add_keymap("alt", "q", { - press = function () - mez.api.exit(); - end + press = function () + mez.api.exit(); + end }) for i = 1, 12 do @@ -51,7 +51,8 @@ end -- }) mez.hook.add_hook("ViewMapPre", { - callback = function() + callback = function(a) + print(a) print("hello world") end }) diff --git a/src/types/events.zig b/src/types/events.zig index 8f6a111..59f37e4 100644 --- a/src/types/events.zig +++ b/src/types/events.zig @@ -41,12 +41,12 @@ pub fn put(self: *Events, key: []const u8, hook: *const Hook) !void { // TODO: figure out deletion // pub fn del(self: *Events, key: ???) !void {} -pub fn exec(self: *Events, event: []const u8) void { +pub fn exec(self: *Events, event: []const u8, args: anytype) void { if (self.events.get(event)) |e| { var node = e.first; while (node) |n| : (node = n.next) { const data: *Node = @fieldParentPtr("node", n); - data.hook.callback(); + data.hook.callback(args); // FIXME: not sure why but for some reason our ll doesn't seem to want to // admit that there's nothing after the first node. diff --git a/src/types/hook.zig b/src/types/hook.zig index ae207df..cfa4c0c 100644 --- a/src/types/hook.zig +++ b/src/types/hook.zig @@ -17,7 +17,13 @@ options: struct { lua_cb_ref_idx: i32, }, -pub fn callback(self: *const Hook) void { +pub fn callback(self: *const Hook, args: anytype) void { + const ArgsType = @TypeOf(args); + const args_type_info = @typeInfo(ArgsType); + if (args_type_info != .@"struct") { + @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); + } + const t = Lua.state.rawGetIndex(zlua.registry_index, self.options.lua_cb_ref_idx); if (t != zlua.LuaType.function) { std.log.err("Failed to call hook, it doesn't have a callback.", .{}); @@ -25,8 +31,19 @@ pub fn callback(self: *const Hook) void { return; } + var i: u8 = 0; + inline for (args, 0..) |field, k| { + // std.log.debug("{any}", .{field}); + + // oh dear god I hope this works + std.log.debug("sldkjf {any}", .{field}); + try Lua.state.pushAny(field); + i = k; + } + // TODO: we need to send some data along with the callback, this data will // change based on the event which the user is hooking into - Lua.state.call(.{ .args = 0, .results = 0 }); + Lua.state.protectedCall(.{ .args = i, .results = 0 }) catch { + }; Lua.state.pop(-1); } diff --git a/src/view.zig b/src/view.zig index 9327de8..80faecd 100644 --- a/src/view.zig +++ b/src/view.zig @@ -100,7 +100,7 @@ fn handleMap(listener: *wl.Listener(void)) void { const view: *View = @fieldParentPtr("map", listener); std.log.debug("Mapping view '{s}'", .{view.xdg_toplevel.title orelse "(unnamed)"}); - server.events.exec("ViewMapPre"); + server.events.exec("ViewMapPre", .{view.id}); view.xdg_toplevel.events.request_fullscreen.add(&view.request_fullscreen); view.xdg_toplevel.events.request_move.add(&view.request_move); @@ -124,7 +124,7 @@ fn handleMap(listener: *wl.Listener(void)) void { view.mapped = true; - server.events.exec("ViewMapPost"); + server.events.exec("ViewMapPost", .{}); } fn handleUnmap(listener: *wl.Listener(void)) void { From 47bcce621de11fc3d36d8a7301e4bc92294de205 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Tue, 25 Nov 2025 16:01:38 -0500 Subject: [PATCH 2/9] lua position, size, focus and z-index (not really) --- README.md | 2 +- build.zig | 12 ++++ ideation.md => notes/ideation.md | 0 runtime/share/mezzaluna/init.lua | 10 ++- src/cursor.zig | 5 +- src/lua/api.zig | 2 +- src/lua/lua.zig | 7 ++ src/lua/view.zig | 116 +++++++++++++++++++++++++++++++ src/output.zig | 45 +++++++++--- src/root.zig | 31 ++++++--- src/seat.zig | 9 --- src/server.zig | 22 +----- src/tag.zig | 32 --------- src/view.zig | 41 +++++++---- 14 files changed, 236 insertions(+), 98 deletions(-) rename ideation.md => notes/ideation.md (100%) create mode 100644 src/lua/view.zig delete mode 100644 src/tag.zig diff --git a/README.md b/README.md index 70dc795..f4b0a18 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ A utensil for chopping herbs, vegetables, or pizza, with a large semicircular blade and a handle at each end. -The idea is that Mezzaluna takes care of the hardwork while leaving configuration, tiling behaviour and general exstensability to be done with easy to write Lua. +The idea is that Mezzaluna takes care of the hardwork while leaving configuration, tiling behaviour and general exstensability to be done with easy to write, lovable Lua. diff --git a/build.zig b/build.zig index 11dbdf3..a48cffe 100644 --- a/build.zig +++ b/build.zig @@ -77,6 +77,18 @@ pub fn build(b: *std.Build) void { b.installArtifact(mez); + const exe_check = b.addExecutable(.{ + .name = "mez", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }) + }); + + const check = b.step("check", "check if mez compiles"); + check.dependOn(&exe_check.step); + const run_step = b.step("run", "Run the app"); const run_cmd = b.addRunArtifact(mez); run_step.dependOn(&run_cmd.step); diff --git a/ideation.md b/notes/ideation.md similarity index 100% rename from ideation.md rename to notes/ideation.md diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index 08f0065..068c35d 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -35,9 +35,17 @@ mez.input.add_keymap("alt", "q", { end }) +mez.input.add_keymap("alt", "v", { + press = function () + local view = mez.view.get_focused_id() + mez.view.set_position(view, 100, 100) + mez.view.set_size(view, 100, 100) + end +}) + for i = 1, 12 do mez.input.add_keymap("ctrl|alt", "XF86Switch_VT_"..i, { - press = function() mez.api.chvt(i) end + press = function() mez.api.change_vt(i) end }) end diff --git a/src/cursor.zig b/src/cursor.zig index 0a076f8..5f52d06 100644 --- a/src/cursor.zig +++ b/src/cursor.zig @@ -75,7 +75,7 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { switch (self.mode) { .passthrough => { if (server.root.viewAt(self.wlr_cursor.x, self.wlr_cursor.y)) |res| { - server.seat.focusView(res.view); + res.view.setFocused(); server.seat.wlr_seat.pointerNotifyEnter(res.surface, res.sx, res.sy); server.seat.wlr_seat.pointerNotifyMotion(time_msec, res.sx, res.sy); @@ -134,8 +134,7 @@ fn handleButton( _ = server.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state); if (server.seat.focused_view) |view| { - server.seat.focusView(view); - server.root.focusView(view); + view.setFocused(); } switch (event.state) { diff --git a/src/lua/api.zig b/src/lua/api.zig index 2d76c88..91d0528 100644 --- a/src/lua/api.zig +++ b/src/lua/api.zig @@ -59,7 +59,7 @@ pub fn exit(L: *zlua.Lua) i32 { return 0; } -pub fn chvt(L: *zlua.Lua) i32 { +pub fn change_vt(L: *zlua.Lua) i32 { L.checkType(1, .number); const f = L.toNumber(-1) catch unreachable; const n: u32 = @intFromFloat(f); diff --git a/src/lua/lua.zig b/src/lua/lua.zig index 4a3ee58..41c3aba 100644 --- a/src/lua/lua.zig +++ b/src/lua/lua.zig @@ -9,6 +9,7 @@ const Fs = @import("fs.zig"); const Input = @import("input.zig"); const Api = @import("api.zig"); const Hook = @import("hook.zig"); +const View = @import("view.zig"); const gpa = std.heap.c_allocator; @@ -75,6 +76,11 @@ pub fn init(self: *Lua) !void { self.state.newLib(api_funcs); self.state.setField(-2, "api"); } + { + const view_funcs = zlua.fnRegsFromType(View); + self.state.newLib(view_funcs); + self.state.setField(-2, "view"); + } } loadRuntimeDir(self) catch |err| { @@ -82,6 +88,7 @@ pub fn init(self: *Lua) !void { std.log.warn("{s}", .{try self.state.toString(-1)}); } }; + loadConfigDir(self) catch |err| { if (err == error.LuaRuntime) { std.log.warn("{s}", .{try self.state.toString(-1)}); diff --git a/src/lua/view.zig b/src/lua/view.zig new file mode 100644 index 0000000..3da0fe2 --- /dev/null +++ b/src/lua/view.zig @@ -0,0 +1,116 @@ +const std = @import("std"); +const zlua = @import("zlua"); +const wlr = @import("wlroots"); + +const View = @import("../view.zig"); + +const gpa = std.heap.c_allocator; + +const server = &@import("../main.zig").server; + +pub fn get_all_ids(L: *zlua.Lua) i32 { + var it = server.root.scene.tree.children.iterator(.forward); + var index: usize = 1; + + L.newTable(); + + while(it.next()) |node| : (index += 1) { + if(node.data == null) continue; + + const view = @as(*View, @ptrCast(@alignCast(node.data.?))); + + L.pushInteger(@intCast(index)); + L.pushInteger(@intCast(view.id)); + L.setTable(1); + } + + return 1; +} + +pub fn get_focused_id(L: *zlua.Lua) i32 { + if(server.seat.focused_view) |view| { + _ = L.pushNumber(@floatFromInt(view.id)); + return 1; + } + + return 0; +} + +pub fn set_position(L: *zlua.Lua) i32 { + const nargs: i32 = L.getTop(); + + std.log.debug("Starting view reposition", .{}); + + if (nargs != 3) { + L.raiseErrorStr("Expected 3 arguments, found {d}", .{nargs}); + return 0; + } + + for (1..@intCast(nargs + 1)) |i| { + L.checkType(@intCast(i), .number); + } + + const view_id: u64 = @as(u64, @intCast(L.toInteger(1) catch unreachable)); + const x: i32 = @as(i32, @intCast(L.toInteger(2) catch unreachable)); + const y: i32 = @as(i32, @intCast(L.toInteger(3) catch unreachable)); + + const view = server.root.viewById(view_id); + if(view == null) { + L.raiseErrorStr("View with id {d} does not exist", .{view_id}); + return 0; + } + + view.?.setPosition(x, y); + + return 0; +} + +pub fn set_size(L: *zlua.Lua) i32 { + const nargs: i32 = L.getTop(); + + if (nargs != 3) { + L.raiseErrorStr("Expected 3 arguments, found {d}", .{nargs}); + return 0; + } + + for (1..@intCast(nargs + 1)) |i| { + L.checkType(@intCast(i), .number); + } + + const view_id: u64 = @as(u64, @intCast(L.toInteger(1) catch unreachable)); + const width: i32 = @as(i32, @intCast(L.toInteger(2) catch unreachable)); + const height: i32 = @as(i32, @intCast(L.toInteger(3) catch unreachable)); + + const view = server.root.viewById(view_id); + if(view == null) { + L.raiseErrorStr("View with id {d} does not exist", .{view_id}); + return 0; + } + + view.?.setSize(width, height); + + return 0; +} + +pub fn raise_to_top(L: *zlua.Lua) i32 { + const nargs: i32 = L.getTop(); + + if(nargs != 1) { + L.raiseErrorStr("Expected 1 arguments, found {d}", .{nargs}); + return 0; + } + + L.checkType(1, .number); + + const view_id: u64 = @intCast(L.toInteger(1) catch unreachable); + + const view = server.root.viewById(view_id); + if(view == null) { + L.raiseErrorStr("View with id {d} does not exist", .{view_id}); + return 0; + } + + view.?.raiseToTop(); + + return 0; +} diff --git a/src/output.zig b/src/output.zig index f165fd4..70f3fcd 100644 --- a/src/output.zig +++ b/src/output.zig @@ -13,6 +13,7 @@ const server = &@import("main.zig").server; focused: bool, wlr_output: *wlr.Output, +state: wlr.Output.State, scene_output: *wlr.SceneOutput, frame: wl.Listener(*wlr.Output) = .init(handleFrame), @@ -20,7 +21,7 @@ request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequest destroy: wl.Listener(*wlr.Output) = .init(handleDestroy), // The wlr.Output should be destroyed by the caller on failure to trigger cleanup. -pub fn create(wlr_output: *wlr.Output) *Output { +pub fn init(wlr_output: *wlr.Output) ?*Output { errdefer Utils.oomPanic(); const output = try gpa.create(Output); @@ -28,25 +29,51 @@ pub fn create(wlr_output: *wlr.Output) *Output { output.* = .{ .focused = false, .wlr_output = wlr_output, - .scene_output = try server.root.scene.createSceneOutput(wlr_output) + .scene_output = try server.root.scene.createSceneOutput(wlr_output), + .state = wlr.Output.State.init() }; wlr_output.events.frame.add(&output.frame); wlr_output.events.request_state.add(&output.request_state); wlr_output.events.destroy.add(&output.destroy); - std.log.debug("adding output: {s}", .{output.wlr_output.name}); + errdefer deinit(output); + + if(!wlr_output.initRender(server.allocator, server.renderer)) { + std.log.err("Unable to start output {s}", .{wlr_output.name}); + return null; + } + + output.state.setEnabled(true); + + if (wlr_output.preferredMode()) |mode| { + output.state.setMode(mode); + } + + if(!wlr_output.commitState(&output.state)) { + std.log.err("Unable to commit state to output {s}", .{wlr_output.name}); + return null; + } + + server.root.addOutput(output); return output; } -// Conflicting name with destroy listener -// Should probably add _listner as a postfix to listeners -// -// pub fn destroy(output: *Output) void { -// gpa.free(output); -// } +pub fn deinit(output: *Output) void { + output.frame.link.remove(); + output.request_state.link.remove(); + output.destroy.remove(); + output.state.finish(); + + output.wlr_output.destroy(); + + gpa.free(output); +} + + +// --------- WlrOutput Event Handlers --------- fn handleRequestState( listener: *wl.Listener(*wlr.Output.event.RequestState), event: *wlr.Output.event.RequestState, diff --git a/src/root.zig b/src/root.zig index 0a54338..11d4305 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,3 +1,6 @@ +/// 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"); @@ -18,8 +21,6 @@ scene_output_layout: *wlr.SceneOutputLayout, output_layout: *wlr.OutputLayout, -views: std.HashMap(u64, *View, std.hash_map.AutoContext(u64), 80), - pub fn init(self: *Root) void { std.log.info("Creating root of mezzaluna\n", .{}); @@ -36,22 +37,36 @@ pub fn init(self: *Root) void { .output_layout = output_layout, .xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server), .scene_output_layout = try scene.attachOutputLayout(output_layout), - .views = .init(gpa) }; } pub fn deinit(self: *Root) void { - var views_it = self.views.iterator(); - while(views_it.next()) |entry| { - entry.value_ptr.*.deinit(); - } + var it = self.scene.tree.children.iterator(.forward); - self.views.deinit(); + while(it.next()) |node| { + if(node.data == null) continue; + + const view: *View = @ptrCast(@alignCast(node.data.?)); + view.deinit(); + } self.output_layout.destroy(); self.scene.tree.node.destroy(); } +pub fn viewById(self: *Root, id: u64) ?*View { + var it = self.scene.tree.children.iterator(.forward); + + while(it.next()) |node| { + if(node.data == null) continue; + + const view: *View = @as(*View, @ptrCast(@alignCast(node.data.?))); + if(view.id == id) return view; + } + + return null; +} + pub fn addOutput(self: *Root, new_output: *Output) void { errdefer Utils.oomPanic(); const layout_output = try self.output_layout.addAuto(new_output.wlr_output); diff --git a/src/seat.zig b/src/seat.zig index 279ae0a..2659fb0 100644 --- a/src/seat.zig +++ b/src/seat.zig @@ -73,15 +73,6 @@ pub fn focusOutput(self: *Seat, output: *Output) void { self.focused_output = output; } -// TODO: Should focusing a view, automaticall focus the output containing it -pub fn focusView(self: *Seat, view: *View) void { - if(self.focused_view) |prev_view| { - prev_view.setFocus(false); - } - - self.focused_view = view; -} - fn handleRequestSetCursor( _: *wl.Listener(*wlr.Seat.event.RequestSetCursor), event: *wlr.Seat.event.RequestSetCursor, diff --git a/src/server.zig b/src/server.zig index 1aafa5a..d42d8b9 100644 --- a/src/server.zig +++ b/src/server.zig @@ -161,6 +161,7 @@ fn handleNewInput( }, } + // We should really only set true capabilities server.seat.wlr_seat.setCapabilities(.{ .pointer = true, .keyboard = true, @@ -171,30 +172,13 @@ fn handleNewOutput( _: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output ) void { - std.log.info("Handling a new output - {s}", .{wlr_output.name}); - - if (!wlr_output.initRender(server.allocator, server.renderer)) return; - - var state = wlr.Output.State.init(); - defer state.finish(); - - state.setEnabled(true); - - if (wlr_output.preferredMode()) |mode| { - state.setMode(mode); - } - if (!wlr_output.commitState(&state)) return; - - const new_output = Output.create(wlr_output); - - server.root.addOutput(new_output); + _ = Output.init(wlr_output); } fn handleNewXdgToplevel( _: *wl.Listener(*wlr.XdgToplevel), xdg_toplevel: *wlr.XdgToplevel ) void { - std.log.debug("Request for new toplevel", .{}); _ = View.initFromTopLevel(xdg_toplevel); } @@ -203,7 +187,7 @@ fn handleNewXdgToplevelDecoration( decoration: *wlr.XdgToplevelDecorationV1 ) void { std.log.debug("Request for decorations", .{}); - if(server.root.views.get(@intFromPtr(decoration.toplevel))) |view| { + if(server.root.viewById(@intFromPtr(decoration.toplevel))) |view| { view.xdg_toplevel_decoration = decoration; } } diff --git a/src/tag.zig b/src/tag.zig deleted file mode 100644 index 63023ac..0000000 --- a/src/tag.zig +++ /dev/null @@ -1,32 +0,0 @@ -const Tag = @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; - -output: *Output, -scene_tree: *wlr.SceneTree, - -views: std.ArrayList(*View), - -pub fn init(output: *Output) Tag { - errdefer Utils.oomPanic(); - return .{ - .output = output, - .scene_tree = try server.root.scene.tree.createSceneTree(), - .views = .initCapacity(gpa, 2), // Probably shouldn't be a magic number - }; -} - -pub fn deinit(self: *Tag) void { - for(self.views.items) |view| { - view - } -} diff --git a/src/view.zig b/src/view.zig index 80faecd..7c7f08c 100644 --- a/src/view.zig +++ b/src/view.zig @@ -51,20 +51,19 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View { errdefer gpa.destroy(self); self.* = .{ - .xdg_toplevel = xdg_toplevel, .focused = false, - .scene_tree = undefined, - .xdg_toplevel_decoration = null, .mapped = false, .id = @intFromPtr(xdg_toplevel), + + .xdg_toplevel = xdg_toplevel, + .scene_tree = undefined, + .xdg_toplevel_decoration = null, }; self.xdg_toplevel.base.surface.events.unmap.add(&self.unmap); - // Add new Toplevel to focused output instead of some random shit - // This is where we find out where to tile the widow, but not NOW - // We need lua for that - // self.scene_tree = try server.root.workspaces.items[0].createSceneXdgSurface(xdg_toplevel.base); + // Add new Toplevel to root of the tree + // Later add to spesified output self.scene_tree = try server.root.scene.tree.createSceneXdgSurface(xdg_toplevel.base); self.scene_tree.node.data = self; @@ -75,8 +74,6 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View { self.xdg_toplevel.base.surface.events.commit.add(&self.commit); self.xdg_toplevel.base.events.new_popup.add(&self.new_popup); - try server.root.views.put(self.id, self); - return self; } @@ -90,9 +87,25 @@ pub fn deinit(self: *View) void { self.request_resize.link.remove(); } -// Handle borders to appropriate colros make necessary notifications -pub fn setFocus(self: *View, focus: bool) void { - self.focused = focus; +pub fn setFocused(self: *View) void { + if(server.seat.focused_view) |prev_view| { + prev_view.focused = false; + } + server.seat.focused_view = self; + self.focused = true; +} + +pub fn raiseToTop(self: *View) void { + self.scene_tree.node.raiseToTop(); +} + +pub fn setPosition(self: *View, x: i32, y: i32) void { + self.scene_tree.node.setPosition(x, y); +} + +pub fn setSize(self: *View, width: i32, height: i32) void { + // This returns a configure serial for verifying the configure + _ = self.xdg_toplevel.setSize(width, height); } // --------- XdgTopLevel event handlers --------- @@ -159,8 +172,6 @@ fn handleDestroy(listener: *wl.Listener(void)) void { view.scene_tree.node.destroy(); // Destroy popups - _ = server.root.views.remove(view.id); - gpa.destroy(view); } @@ -169,7 +180,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { // On the first commit, send a configure to tell the client it can proceed if (view.xdg_toplevel.base.initial_commit) { - _ = view.xdg_toplevel.setSize(640, 360); // 0,0 means "you decide the size" + view.setSize(640, 360); } } From e142b6027f75cb934274ddd25449a47f3c095872 Mon Sep 17 00:00:00 2001 From: Squibid Date: Sat, 22 Nov 2025 18:27:41 -0500 Subject: [PATCH 3/9] fix crashing when a view is resized too small this is done by limiting to a minimum of 10x10 pixels, we may make this configurable using lua in the future --- src/cursor.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cursor.zig b/src/cursor.zig index 5f52d06..2309ce2 100644 --- a/src/cursor.zig +++ b/src/cursor.zig @@ -100,8 +100,9 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { if(focused_view) |view| { _ = view.xdg_toplevel.setSize( - @intCast(@as(c_int, @intFromFloat(self.wlr_cursor.x)) - view.scene_tree.node.x), - @intCast(@as(c_int, @intFromFloat(self.wlr_cursor.y)) - view.scene_tree.node.y) + // TODO: configure the min and max using lua? + std.math.clamp(@as(c_int, @as(i32, @intFromFloat(self.wlr_cursor.x)) - view.scene_tree.node.x), 10, std.math.maxInt(i32)), + std.math.clamp(@as(c_int, @as(i32, @intFromFloat(self.wlr_cursor.y)) - view.scene_tree.node.y), 10, std.math.maxInt(i32)) ); } }, From 514db29d7e03d4eab9f7481da45038b3d3f1805a Mon Sep 17 00:00:00 2001 From: Squibid Date: Sat, 22 Nov 2025 18:48:16 -0500 Subject: [PATCH 4/9] update log statement --- src/view.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view.zig b/src/view.zig index 7c7f08c..24bb2ee 100644 --- a/src/view.zig +++ b/src/view.zig @@ -268,7 +268,7 @@ fn handleSetAppId( ) void { const view: *View = @fieldParentPtr("set_app_id", listener); _ = view; - std.log.err("Unimplemented request maximize", .{}); + std.log.err("Unimplemented set appid", .{}); } fn handleSetTitle( From 96865dcfa3d4edccd4358da71f60d26849dfdd9a Mon Sep 17 00:00:00 2001 From: Squibid Date: Sat, 22 Nov 2025 18:49:46 -0500 Subject: [PATCH 5/9] remove duplicate function --- src/view.zig | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/view.zig b/src/view.zig index 24bb2ee..772d323 100644 --- a/src/view.zig +++ b/src/view.zig @@ -255,14 +255,6 @@ fn handleRequestMinimize( std.log.err("Unimplemented request minimize", .{}); } -fn handleRequestMaximize( - listener: *wl.Listener(void) -) void { - const view: *View = @fieldParentPtr("request_fullscreen", listener); - _ = view; - std.log.err("Unimplemented request maximize", .{}); -} - fn handleSetAppId( listener: *wl.Listener(void) ) void { From 08d7f1f1761bfacea0556cd5f344f0b55be83c06 Mon Sep 17 00:00:00 2001 From: Squibid Date: Sat, 22 Nov 2025 20:46:57 -0500 Subject: [PATCH 6/9] default view moving shouldn't clamp to the visible area --- src/cursor.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cursor.zig b/src/cursor.zig index 2309ce2..44c8bc9 100644 --- a/src/cursor.zig +++ b/src/cursor.zig @@ -89,8 +89,11 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { if(focused_view) |view| { view.scene_tree.node.setPosition( - std.math.clamp(@as(c_int, @intFromFloat(self.wlr_cursor.x)) - self.drag_view_offset_x, 0, std.math.maxInt(u32)), - std.math.clamp(@as(c_int, @intFromFloat(self.wlr_cursor.y)) - self.drag_view_offset_y, 0, std.math.maxInt(u32)) + // TODO: add a lua option to configure the behavior of this, by + // default it will be the following: + @as(c_int, @intFromFloat(self.wlr_cursor.x)) - self.drag_view_offset_x, + @as(c_int, @intFromFloat(self.wlr_cursor.y)) - self.drag_view_offset_y + // and the user should be able to configure if it clamps or not ); } }, From d255f63ac7792327d4e140b74db19fb10a28cdc2 Mon Sep 17 00:00:00 2001 From: Squibid Date: Sun, 23 Nov 2025 17:08:44 -0500 Subject: [PATCH 7/9] generate if branching for modifier keys at comptime --- src/lua/input.zig | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/lua/input.zig b/src/lua/input.zig index 5ecea31..8b42796 100644 --- a/src/lua/input.zig +++ b/src/lua/input.zig @@ -14,22 +14,10 @@ fn parse_modkeys(modStr: []const u8) wlr.Keyboard.ModifierMask { var it = std.mem.splitScalar(u8, modStr, '|'); var modifiers = wlr.Keyboard.ModifierMask{}; while (it.next()) |m| { - if (std.mem.eql(u8, m, "shift")) { - modifiers.shift = true; - } else if (std.mem.eql(u8, m, "caps")) { - modifiers.caps = true; - } else if (std.mem.eql(u8, m, "ctrl")) { - modifiers.ctrl = true; - } else if (std.mem.eql(u8, m, "alt")) { - modifiers.alt = true; - } else if (std.mem.eql(u8, m, "mod2")) { - modifiers.mod2 = true; - } else if (std.mem.eql(u8, m, "mod3")) { - modifiers.mod3 = true; - } else if (std.mem.eql(u8, m, "logo")) { - modifiers.logo = true; - } else if (std.mem.eql(u8, m, "mod5")) { - modifiers.mod5 = true; + inline for (std.meta.fields(@TypeOf(modifiers))) |f| { + if (f.type == bool and std.mem.eql(u8, m, f.name)) { + @field(modifiers, f.name) = true; + } } } From 91578d583ad2e79cf5924c03d9340fce030f1e79 Mon Sep 17 00:00:00 2001 From: Squibid Date: Sun, 23 Nov 2025 21:05:29 -0500 Subject: [PATCH 8/9] rename mez.hook.add_hook -> mez.hook.add --- runtime/share/mezzaluna/init.lua | 2 +- src/lua/hook.zig | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index 068c35d..f52fc52 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -58,7 +58,7 @@ end -- end -- }) -mez.hook.add_hook("ViewMapPre", { +mez.hook.add("ViewMapPre", { callback = function(a) print(a) print("hello world") diff --git a/src/lua/hook.zig b/src/lua/hook.zig index cf26c57..6273f2f 100644 --- a/src/lua/hook.zig +++ b/src/lua/hook.zig @@ -9,7 +9,7 @@ const zlua = @import("zlua"); const gpa = std.heap.c_allocator; const server = &@import("../main.zig").server; -pub fn add_hook(L: *zlua.Lua) i32 { +pub fn add(L: *zlua.Lua) i32 { L.checkType(2, .table); var hook: *THook = gpa.create(THook) catch { @@ -22,7 +22,7 @@ pub fn add_hook(L: *zlua.Lua) i32 { }; // We support both a string and a table of strings as the first value of - // add_hook. Regardless of which type is passed in we create an arraylist of + // add. Regardless of which type is passed in we create an arraylist of // []const u8's if (L.isTable(1)) { L.pushNil(); @@ -74,7 +74,7 @@ pub fn add_hook(L: *zlua.Lua) i32 { return 0; } -pub fn del_hook(L: *zlua.Lua) i32 { +pub fn del(L: *zlua.Lua) i32 { // TODO: impl _ = L; return 0; From 0bb1ba29632ecce7810cdb22af1f5aa539a0fe01 Mon Sep 17 00:00:00 2001 From: Squibid Date: Tue, 25 Nov 2025 16:48:04 -0500 Subject: [PATCH 9/9] we can now pass arbitrary zig data to lua through hooks --- runtime/share/mezzaluna/init.lua | 5 ++--- src/types/hook.zig | 11 +++-------- src/view.zig | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index f52fc52..ec74bdd 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -59,8 +59,7 @@ end -- }) mez.hook.add("ViewMapPre", { - callback = function(a) - print(a) - print("hello world") + callback = function(v) + mez.view.set_size(v, 1000, 1000) end }) diff --git a/src/types/hook.zig b/src/types/hook.zig index cfa4c0c..7b2693f 100644 --- a/src/types/hook.zig +++ b/src/types/hook.zig @@ -31,19 +31,14 @@ pub fn callback(self: *const Hook, args: anytype) void { return; } + // allow passing any arguments to the lua hook var i: u8 = 0; - inline for (args, 0..) |field, k| { - // std.log.debug("{any}", .{field}); - - // oh dear god I hope this works - std.log.debug("sldkjf {any}", .{field}); + inline for (args, 1..) |field, k| { try Lua.state.pushAny(field); i = k; } - // TODO: we need to send some data along with the callback, this data will - // change based on the event which the user is hooking into - Lua.state.protectedCall(.{ .args = i, .results = 0 }) catch { + Lua.state.protectedCall(.{ .args = i }) catch { }; Lua.state.pop(-1); } diff --git a/src/view.zig b/src/view.zig index 772d323..61aa3a3 100644 --- a/src/view.zig +++ b/src/view.zig @@ -137,7 +137,7 @@ fn handleMap(listener: *wl.Listener(void)) void { view.mapped = true; - server.events.exec("ViewMapPost", .{}); + server.events.exec("ViewMapPost", .{view.id}); } fn handleUnmap(listener: *wl.Listener(void)) void {