From b03738534b2b008b978a4bb5562a45f355f2366e Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 19 Dec 2025 23:22:20 -0500 Subject: [PATCH] layout manager --- runtime/share/mezzaluna/init.lua | 1 + runtime/share/mezzaluna/layout_manager.lua | 68 +++ runtime/share/mezzaluna/master.lua | 478 +++++++-------------- src/lua/LuaUtils.zig | 12 + src/lua/Output.zig | 55 +++ 5 files changed, 300 insertions(+), 314 deletions(-) create mode 100644 runtime/share/mezzaluna/layout_manager.lua diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index da046b8..896dc18 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -11,6 +11,7 @@ end -- allow loading files in the runtime directory package.path = package.path..";"..mez.fs.joinpath(mez.path.runtime, "?.lua") mez.inspect = require("inspect").inspect +mez.layout_manager = require("layout_manager") mez.path.base_config = mez.fs.joinpath(mez.path.runtime, "master.lua") diff --git a/runtime/share/mezzaluna/layout_manager.lua b/runtime/share/mezzaluna/layout_manager.lua new file mode 100644 index 0000000..b7f6734 --- /dev/null +++ b/runtime/share/mezzaluna/layout_manager.lua @@ -0,0 +1,68 @@ +--- This is a wrapper around mez's hooks and keybinds to allow users to create +--- layouts without multiple different layouts conflicting. +--- +--- TODO: this may be overridden by the user by setting mez.layout prior to the +--- runtime loading + +---@class layout a singular layout +---@field name string the name of the layout +---@field callback function this is the function which is run when changes to current state have been made +---@field events string[]? this is a list of the events that callback will be run on + +---@class layout_manager manage all your layouts +---@field layouts layout[] all the layouts that are registered +---@field current_layout layout the currently active layout +local layout_manager = { + layouts = {}, + --- the baseline events for a new layout + events = { + "ViewMapPre", + "ViewUnmapPost", + -- "ViewPointerMotion", + } +} + +--- create a new layout +---@param options layout +---@return layout your_layout returns your new layout +function layout_manager.new_layout(options) + local layout = {} + + layout.name = options.name + layout.callback = options.callback + layout.events = layout_manager.events + + -- store the new layout in the layout list + layout_manager.layouts[#layout_manager.layouts + 1] = layout + return layout +end + +--- set the current layout +---@param layout_name string the name of the layout already created in the layout_manager +function layout_manager.set_layout(layout_name) + local layout = nil + for _, l in ipairs(layout_manager.layouts) do + if l.name == layout_name then + layout = l + end + end + assert(layout, "layout '"..layout_name.."' does not exist") + + if layout_manager.current_layout then + local current_layout = layout_manager.current_layout + -- TODO: this actually doesn't exist in mez just yet + -- mez.hook.del(current_layout) + end + mez.hook.add(layout.events, { + callback = function () + print("layouting") + for _, output in ipairs(mez.output.get_all_ids()) do + layout.callback(output) + end + print("layouting done") + end, + }) + layout.callback() +end + +return layout_manager diff --git a/runtime/share/mezzaluna/master.lua b/runtime/share/mezzaluna/master.lua index ae71be8..f60decf 100644 --- a/runtime/share/mezzaluna/master.lua +++ b/runtime/share/mezzaluna/master.lua @@ -1,341 +1,191 @@ -mez.input.add_keymap("alt", "g", { - press = function () - for _, id in ipairs(mez.view.get_all_ids()) do - print(id) +mez.layout_manager.new_layout({ + name = "master", + callback = function(output) + local mw, my, ty; + + local mon = mez.output.get_resolution(0) + local nmaster = 1 + local mfact = 0.5 + + local view_ids = {} + + local ok, ids = pcall(mez.output.get_all_views, 0) + if not ok or ids == nil then + print("nillllll") + return end - end -}) + print("continue") -mez.input.add_keymap("alt", "i", { - press = function () - local coroutine = require("coroutine") - print(coroutine) - local co = coroutine.create(function () - print("starting coroutine") - local view_id = mez.view.get_focused_id() - - local size = mez.view.get_size(view_id) - local pos = mez.view.get_position(view_id) - local res = mez.output.get_resolution(0) - - local x_pos = pos.x - local y_pos = pos.y - local x_vel = 10 - local y_vel = 10 - - while true do - x_pos = x_pos + x_vel - y_pos = y_pos + y_vel - - if x_pos + size.width > res.width or x_pos < 0 then - x_vel = x_vel * -1 - end - - if y_pos + size.height > res.height or y_pos < 0 then - y_vel = y_vel * -1 - end - - print("(" .. x_pos .. ", " .. y_pos .. ")") - mez.view.set_position(view_id, x_pos, y_pos) - end - end) - coroutine.resume(co) - end -}) - -local master = function() - local config = { - tag_count = 5, - } - - local ctx = { - master_ratio = 0.5, - tags = {}, - tag_id = 1, - } - - local tile_onscreen = function(tag_id, res) - if ctx.tags[tag_id].master == nil then + print(mez.inspect(ids)) + for _, v in ipairs(ids) do + view_ids[#view_ids + 1] = v + end + if #view_ids == 0 then return end - mez.view.set_enabled(ctx.tags[tag_id].master, true) - if #ctx.tags[tag_id].stack == 0 then - mez.view.set_size(ctx.tags[tag_id].master, res.width, res.height) - mez.view.set_position(ctx.tags[tag_id].master, 0, 0) + if #view_ids > nmaster then + mw = (nmaster and math.floor((mon.width * mfact) + 0.5)) or 0 else - mez.view.set_size(ctx.tags[tag_id].master, res.width * ctx.master_ratio, res.height) - mez.view.set_position(ctx.tags[tag_id].master, 0, 0) - - for i, stack_id in ipairs(ctx.tags[tag_id].stack) do - mez.view.set_enabled(stack_id, true) - mez.view.set_size(stack_id, res.width * (1 - ctx.master_ratio), res.height / #ctx.tags[tag_id].stack) - mez.view.set_position(stack_id, res.width * ctx.master_ratio, (res.height / #ctx.tags[tag_id].stack * (i - 1))) - end - end - end - - local tile_offscreen = function(tag_id, res) - if ctx.tags[tag_id].master == nil then - return + mw = mon.width end - mez.view.set_enabled(ctx.tags[tag_id].master, false) - for _, view in ipairs(ctx.tags[tag_id].stack) do - mez.view.set_enabled(view, false) - end - end + print("slkdjflksdjflksjdflkjsdf"..mw) - local tile_all = function () - local res = mez.output.get_resolution(0) - - for id = 1,config.tag_count do - if id == ctx.tag_id then - tile_onscreen(id, res) + my, ty = 0, 0 + -- wl_list_for_each(c, &clients, link) { + -- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + -- continue; + for i, view in ipairs(view_ids) do + if i <= nmaster then + mez.view.set_position(view, 0, 0 + my) + mez.view.set_size(view, mw, (mon.height - my) / (math.min(#view_ids, nmaster) - (i - 1))) + my = my + mez.view.get_size(view).height else - tile_offscreen(id, res) + mez.view.set_position(view, 0 + mw, 0 + ty) + mez.view.set_size(view, mon.width + mw, (mon.height - ty) / (#view_ids - i)) + ty = ty + mez.view.get_size(view).height end end end +}) - for i = 1,config.tag_count do - ctx.tags[#ctx.tags + 1] = { - stack = {}, - master = nil, - } - mez.input.add_keymap("alt", "" .. i, { - press = function () - ctx.tag_id = i - if ctx.tags[i].master then - mez.view.set_focused(ctx.tags[i].master) - else - mez.view.set_focused(nil) - end +-- mez.input.add_keymap("alt", "Return", { +-- press = function() +-- local focused = mez.view.get_focused_id() +-- +-- if focused == ctx.tags[ctx.tag_id].master then return end +-- +-- for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do +-- if focused == id then +-- local t = ctx.tags[ctx.tag_id].master +-- ctx.tags[ctx.tag_id].master = ctx.tags[ctx.tag_id].stack[i] +-- ctx.tags[ctx.tag_id].stack[i] = t +-- end +-- end +-- +-- tile_all() +-- end, +-- }) +-- +-- mez.input.add_keymap("alt", "j", { +-- press = function () +-- local focused = mez.view.get_focused_id() +-- +-- if focused == ctx.tags[ctx.tag_id].master then +-- mez.view.set_focused(ctx.tags[ctx.tag_id].stack[1]) +-- elseif focused == ctx.tags[ctx.tag_id].stack[#ctx.tags[ctx.tag_id].stack] then +-- mez.view.set_focused(ctx.tags[ctx.tag_id].master) +-- else +-- for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do +-- -- TODO: use table.find +-- if focused == id then +-- mez.view.set_focused(ctx.tags[ctx.tag_id].stack[i + 1]) +-- end +-- end +-- end +-- end +-- }) +-- +-- mez.input.add_keymap("alt", "k", { +-- press = function () +-- local focused = mez.view.get_focused_id() +-- +-- if focused == ctx.tags[ctx.tag_id].master then +-- mez.view.set_focused(ctx.tags[ctx.tag_id].stack[#ctx.tags[ctx.tag_id].stack]) +-- elseif focused == ctx.tags[ctx.tag_id].stack[1] then +-- mez.view.set_focused(ctx.tags[ctx.tag_id].master) +-- else +-- for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do +-- -- TODO: use table.find +-- if focused == id then +-- mez.view.set_focused(ctx.tags[ctx.tag_id].stack[i - 1]) +-- end +-- end +-- end +-- end +-- }) +-- +-- mez.input.add_keymap("alt", "h", { +-- press = function() +-- if ctx.master_ratio > 0.15 then +-- ctx.master_ratio = ctx.master_ratio - 0.05 +-- tile_all() +-- end +-- end +-- }) +-- +-- mez.input.add_keymap("alt", "l", { +-- press = function() +-- if ctx.master_ratio < 0.85 then +-- ctx.master_ratio = ctx.master_ratio + 0.05 +-- tile_all() +-- end +-- end +-- }) - tile_all() - end - }) + + +mez.layout_manager.set_layout("master") + + + + + + +mez.input.add_keymap("alt", "p", { + press = function() + mez.api.spawn("wmenu-run") + end, +}) + +mez.input.add_keymap("alt", "b", { + press = function() + mez.api.spawn("swaybg -i ~/Images/wallpapers/void/gruv_void.png") + end, +}) + +mez.input.add_keymap("alt|shift", "Return", { + press = function() + mez.api.spawn("alacritty") + end, +}) + +mez.input.add_keymap("alt|shift", "C", { + press = function () + mez.view.close(0) end +}) - mez.hook.add("ServerStartPost", { - callback = function() - print("ServerStartPost") - print("Doesn't do anything :(") - end - }) - - mez.hook.add("ViewMapPre", { - callback = function(v) - if ctx.tags[ctx.tag_id].master == nil then - ctx.tags[ctx.tag_id].master = v - else - table.insert(ctx.tags[ctx.tag_id].stack, #ctx.tags[ctx.tag_id].stack + 1, v) - end - - mez.view.set_focused(v) - - tile_all() - end - }) - - mez.hook.add("ViewUnmapPost", { - callback = function(v) - if v == ctx.tags[ctx.tag_id].master then - if #ctx.tags[ctx.tag_id].stack > 0 then - ctx.tags[ctx.tag_id].master = table.remove(ctx.tags[ctx.tag_id].stack, 1) - mez.view.set_focused(ctx.tags[ctx.tag_id].master) - else - ctx.tags[ctx.tag_id].master = nil - end - else - for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do - if id == v then - if i == 1 then - mez.view.set_focused(ctx.tags[ctx.tag_id].master) - elseif i == #ctx.tags[ctx.tag_id].stack then - mez.view.set_focused(ctx.tags[ctx.tag_id].stack[i - 1]) - else - mez.view.set_focused(ctx.tags[ctx.tag_id].stack[i + 1]) - end - - table.remove(ctx.tags[ctx.tag_id].stack, i) - end - end - end - - tile_all() - end - }) - - mez.input.add_keymap("alt", "p", { - press = function() - mez.api.spawn("wmenu-run") - end, - }) - - mez.input.add_keymap("alt", "b", { - press = function() - mez.api.spawn("swaybg -i ~/Images/wallpapers/void/gruv_void.png") - end, - }) - - mez.input.add_keymap("alt|shift", "Return", { - press = function() - mez.api.spawn("alacritty") - end, - }) - - mez.input.add_keymap("alt|shift", "C", { - press = function () - mez.view.close(0) - end - }) - - mez.input.add_keymap("alt|shift", "q", { - press = function () - mez.api.exit(); - end - }) - - mez.input.add_keymap("alt", "Return", { - press = function() - local focused = mez.view.get_focused_id() - - if focused == ctx.tags[ctx.tag_id].master then return end - - for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do - if focused == id then - local t = ctx.tags[ctx.tag_id].master - ctx.tags[ctx.tag_id].master = ctx.tags[ctx.tag_id].stack[i] - ctx.tags[ctx.tag_id].stack[i] = t - end - end - - tile_all() - end, - }) - - mez.input.add_keymap("alt", "j", { - press = function () - local focused = mez.view.get_focused_id() - - if focused == ctx.tags[ctx.tag_id].master then - mez.view.set_focused(ctx.tags[ctx.tag_id].stack[1]) - elseif focused == ctx.tags[ctx.tag_id].stack[#ctx.tags[ctx.tag_id].stack] then - mez.view.set_focused(ctx.tags[ctx.tag_id].master) - else - for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do - -- TODO: use table.find - if focused == id then - mez.view.set_focused(ctx.tags[ctx.tag_id].stack[i + 1]) - end - end - end - end - }) - - mez.input.add_keymap("alt", "k", { - press = function () - local focused = mez.view.get_focused_id() - - if focused == ctx.tags[ctx.tag_id].master then - mez.view.set_focused(ctx.tags[ctx.tag_id].stack[#ctx.tags[ctx.tag_id].stack]) - elseif focused == ctx.tags[ctx.tag_id].stack[1] then - mez.view.set_focused(ctx.tags[ctx.tag_id].master) - else - for i, id in ipairs(ctx.tags[ctx.tag_id].stack) do - -- TODO: use table.find - if focused == id then - mez.view.set_focused(ctx.tags[ctx.tag_id].stack[i - 1]) - end - end - end - end - }) - - mez.input.add_keymap("alt", "h", { - press = function() - if ctx.master_ratio > 0.15 then - ctx.master_ratio = ctx.master_ratio - 0.05 - tile_all() - end - end - }) - - mez.input.add_keymap("alt", "l", { - press = function() - if ctx.master_ratio < 0.85 then - ctx.master_ratio = ctx.master_ratio + 0.05 - tile_all() - end - end - }) - - mez.input.add_keymap("alt", "Tab", { - press = function () - local focused = mez.view.get_focused_id() - local all = mez.view.get_all_ids() - - for _, id in ipairs(all) do - if id ~= focused then - mez.view.set_focused(id) - return - end - end - end - }) - - local fullscreen = function (view_id) - mez.view.toggle_fullscreen(view_id) - tile_all() +mez.hook.add("ViewPointerMotion", { + callback = function (view_id, cursor_x, cursor_y) + mez.view.set_focused(view_id) end +}) - mez.input.add_keymap("alt|shift", "F", { - press = function() fullscreen(0) end - }) - - mez.hook.add("ViewRequestFullscreen", { - callback = fullscreen - }) - - mez.hook.add("ViewPointerMotion", { - callback = function (view_id, cursor_x, cursor_y) - mez.view.set_focused(view_id) - end - }) - - for i = 1, 12 do - mez.input.add_keymap("ctrl|alt", "XF86Switch_VT_"..i, { - press = function() mez.api.change_vt(i) end - }) +mez.input.add_keymap("alt|shift", "q", { + press = function () + mez.api.exit(); end -end +}) -master() +mez.input.add_keymap("alt", "Tab", { + press = function () + local focused = mez.view.get_focused_id() + local all = mez.view.get_all_ids() -function print_table(tbl, indent, seen) - indent = indent or 0 - seen = seen or {} - - -- Prevent infinite loops from circular references - if seen[tbl] then - print(string.rep(" ", indent) .. "...(circular reference)") + for _, id in ipairs(all) do + if id ~= focused then + mez.view.set_focused(id) return + end end - seen[tbl] = true + end +}) - for key, value in pairs(tbl) do - local formatting = string.rep(" ", indent) .. tostring(key) .. ": " - - if type(value) == "table" then - print(formatting .. "{") - print_table(value, indent + 1, seen) - print(string.rep(" ", indent) .. "}") - elseif type(value) == "string" then - print(formatting .. '"' .. value .. '"') - else - print(formatting .. tostring(value)) - end - end +for i = 1, 12 do + mez.input.add_keymap("ctrl|alt", "XF86Switch_VT_"..i, { + press = function() mez.api.change_vt(i) end + }) end diff --git a/src/lua/LuaUtils.zig b/src/lua/LuaUtils.zig index c924ec6..3f1879a 100644 --- a/src/lua/LuaUtils.zig +++ b/src/lua/LuaUtils.zig @@ -4,6 +4,7 @@ const std = @import("std"); const zlua = @import("zlua"); const View = @import("../View.zig"); +const Output = @import("../Output.zig"); const server = &@import("../main.zig").server; @@ -58,3 +59,14 @@ pub fn viewById(view_id: u64) ?*View { } return null; } + +pub fn outputById(output_id: u64) ?*Output { + if (output_id == 0) { + if(server.seat.focused_output) |output| { + return output; + } + } else { + return server.root.outputById(output_id); + } + return null; +} diff --git a/src/lua/Output.zig b/src/lua/Output.zig index 1c16dbe..c742d84 100644 --- a/src/lua/Output.zig +++ b/src/lua/Output.zig @@ -1,8 +1,10 @@ const std = @import("std"); const zlua = @import("zlua"); +const wlr = @import("wlroots"); const Output = @import("../Output.zig"); const LuaUtils = @import("LuaUtils.zig"); +const SceneNodeData = @import("../SceneNodeData.zig").SceneNodeData; const server = &@import("../main.zig").server; @@ -33,6 +35,59 @@ pub fn get_all_ids(L: *zlua.Lua) i32 { return 1; } +/// ---Get the view ids for all views on an output +/// ---@param output_id output_id +/// ---@return view_id[]? +pub fn get_all_views(L: *zlua.Lua) i32 { + const output_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch output_id_err(L); + var index: i32 = 1; + + L.newTable(); + + if(LuaUtils.outputById(output_id)) |o| { + if(o.wlr_output.data == null) { + L.pushNil(); + return 1; + } + const output: *Output = @ptrCast(@alignCast(o.wlr_output.data.?)); + if (!output.state.enabled) { + std.log.debug("ts not enabled", .{}); + L.pushNil(); + return 1; + } + + const layers = [_]*wlr.SceneTree{ + output.layers.content, + output.layers.fullscreen, + }; + + for(layers) |layer| { + if(layer.children.length() == 0) continue; + if(@intFromPtr(layer) == 0) { + std.log.debug("ts is literally a null ptr", .{}); + continue; + } + + var view_it = layer.children.iterator(.forward); + + while(view_it.next()) |v| { + if(v.data == null) continue; + const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(v.data.?)); + + if(scene_node_data.* == .view) { + L.pushInteger(@intCast(index)); + L.pushInteger(@intCast(scene_node_data.view.id)); + L.setTable(-3); + + index += 1; + } + } + } + } + + return 1; +} + /// ---Get the id for the focused output /// ---@return output_id? pub fn get_focused_id(L: *zlua.Lua) i32 {