aboutsummaryrefslogtreecommitdiffstats
path: root/config/hypr/scripts/float.all.samesize.lua
diff options
context:
space:
mode:
authorDon Williams <don.e.williams@gmail.com>2026-08-25 13:33:41 -0400
committerPinapelz <yukais@pinapelz.com>2026-08-29 21:41:34 -0700
commit4d8a5ee9468a1668075cec691be47dded740d18a (patch)
tree3fbd27904731a527d24c695f89314e5fbb3a4146 /config/hypr/scripts/float.all.samesize.lua
parentfeb41d8014e8cdd28836104d21931c64ba0391ae (diff)
Added float all same size lua script
Signed-off-by: Don Williams <don.e.williams@gmail.com> On branch development Your branch is up to date with 'origin/development'. Changes to be committed: new file: config/hypr/scripts/float.all.samesize.lua
Diffstat (limited to 'config/hypr/scripts/float.all.samesize.lua')
-rwxr-xr-xconfig/hypr/scripts/float.all.samesize.lua267
1 files changed, 267 insertions, 0 deletions
diff --git a/config/hypr/scripts/float.all.samesize.lua b/config/hypr/scripts/float.all.samesize.lua
new file mode 100755
index 00000000..ee7cec69
--- /dev/null
+++ b/config/hypr/scripts/float.all.samesize.lua
@@ -0,0 +1,267 @@
+#!/usr/bin/env lua
+-- ==================================================
+-- KoolDots (2026)
+-- Project URL: https://github.com/LinuxBeginnings
+-- License: GNU GPLv3
+-- SPDX-License-Identifier: GPL-3.0-or-later
+-- ==================================================
+-- Float all windows in the active workspace to uniform dimensions
+-- dynamically computed from monitor resolution, reserved bar margins,
+-- and the number of active windows.
+-- Exits safely if monocle layout is active.
+
+-- Lightweight embedded JSON decoder to guarantee standalone execution
+-- without requiring external C libraries (e.g. cjson)
+local function parse_json(str)
+ if not str or str:match("^%s*$") then return nil end
+ local pos = 1
+ local function skip_ws()
+ pos = str:find("%S", pos) or (#str + 1)
+ end
+ local parse_val
+ local function parse_str()
+ local res = {}
+ pos = pos + 1
+ while pos <= #str do
+ local c = str:sub(pos, pos)
+ if c == '"' then
+ pos = pos + 1
+ return table.concat(res)
+ elseif c == '\\' then
+ pos = pos + 1
+ local esc = str:sub(pos, pos)
+ if esc == '"' or esc == '\\' or esc == '/' then
+ table.insert(res, esc)
+ elseif esc == 'b' then table.insert(res, '\b')
+ elseif esc == 'f' then table.insert(res, '\f')
+ elseif esc == 'n' then table.insert(res, '\n')
+ elseif esc == 'r' then table.insert(res, '\r')
+ elseif esc == 't' then table.insert(res, '\t')
+ elseif esc == 'u' then
+ local hex = str:sub(pos + 1, pos + 4)
+ pos = pos + 4
+ local code = tonumber(hex, 16)
+ if code and code < 128 then
+ table.insert(res, string.char(code))
+ else
+ table.insert(res, '?')
+ end
+ end
+ pos = pos + 1
+ else
+ local s = str:find('["\\]', pos) or (#str + 1)
+ table.insert(res, str:sub(pos, s - 1))
+ pos = s
+ end
+ end
+ error("unterminated JSON string")
+ end
+ local function parse_num()
+ local s, e = str:find("^-?%d+%.?%d*[eE]?[+-]?%d*", pos)
+ if not s then error("invalid JSON number at position " .. pos) end
+ pos = e + 1
+ return tonumber(str:sub(s, e))
+ end
+ local function parse_arr()
+ local arr = {}
+ pos = pos + 1
+ skip_ws()
+ if str:sub(pos, pos) == "]" then pos = pos + 1 return arr end
+ while pos <= #str do
+ table.insert(arr, parse_val())
+ skip_ws()
+ local c = str:sub(pos, pos)
+ if c == "]" then pos = pos + 1 return arr
+ elseif c == "," then pos = pos + 1 skip_ws()
+ else error("expected ',' or ']' at position " .. pos) end
+ end
+ end
+ local function parse_obj()
+ local obj = {}
+ pos = pos + 1
+ skip_ws()
+ if str:sub(pos, pos) == "}" then pos = pos + 1 return obj end
+ while pos <= #str do
+ skip_ws()
+ if str:sub(pos, pos) ~= '"' then error("expected JSON key at position " .. pos) end
+ local k = parse_str()
+ skip_ws()
+ if str:sub(pos, pos) ~= ":" then error("expected ':' at position " .. pos) end
+ pos = pos + 1
+ skip_ws()
+ obj[k] = parse_val()
+ skip_ws()
+ local c = str:sub(pos, pos)
+ if c == "}" then pos = pos + 1 return obj
+ elseif c == "," then pos = pos + 1 skip_ws()
+ else error("expected ',' or '}' at position " .. pos) end
+ end
+ end
+ parse_val = function()
+ skip_ws()
+ local c = str:sub(pos, pos)
+ if c == '"' then return parse_str()
+ elseif c == "{" then return parse_obj()
+ elseif c == "[" then return parse_arr()
+ elseif c == "t" and str:sub(pos, pos + 3) == "true" then pos = pos + 4 return true
+ elseif c == "f" and str:sub(pos, pos + 4) == "false" then pos = pos + 5 return false
+ elseif c == "n" and str:sub(pos, pos + 3) == "null" then pos = pos + 4 return nil
+ elseif c and c:find("[%-%d]") then return parse_num()
+ else error("unexpected character '" .. tostring(c) .. "' at position " .. pos) end
+ end
+
+ local ok, res = pcall(parse_val)
+ return ok and res or nil
+end
+
+-- Helper to run commands and capture JSON output
+local function exec_json(cmd)
+ local handle = io.popen(cmd)
+ if not handle then return nil end
+ local result = handle:read("*a")
+ handle:close()
+ if not result or result == "" then return nil end
+
+ -- Try fast native/library decoders first, fallback to pure Lua parser
+ if _G.vim and vim.fn and vim.fn.json_decode then
+ local ok, json = pcall(vim.fn.json_decode, result)
+ if ok then return json end
+ end
+
+ local ok_cjson, cjson = pcall(require, "cjson")
+ if ok_cjson and cjson and cjson.decode then
+ local ok, json = pcall(cjson.decode, result)
+ if ok then return json end
+ end
+
+ local ok_luna, lunajson = pcall(require, "lunajson")
+ if ok_luna and lunajson and lunajson.decode then
+ local ok, json = pcall(lunajson.decode, result)
+ if ok then return json end
+ end
+
+ return parse_json(result)
+end
+
+-- 1. Fetch active workspace, monitor details, and clients
+local active_ws = exec_json("hyprctl -j activeworkspace")
+if not active_ws or not active_ws.id then
+ os.exit(1)
+end
+
+-- 2. Check for Monocle layout
+-- Floating all windows is counterproductive in Monocle mode (designed for full focus / single window visibility)
+local layout = active_ws.tiledLayout or active_ws.tiled_layout or active_ws.layout
+if not layout or layout == "" then
+ local general_opt = exec_json("hyprctl -j getoption general:layout")
+ if general_opt and general_opt.str then
+ layout = general_opt.str
+ end
+end
+
+if layout == "monocle" then
+ io.stderr:write("Monocle layout is active on workspace " .. tostring(active_ws.id) .. " - skipping float all windows.\n")
+ os.execute("notify-send -e -u low -t 2000 'Float Windows' 'Skipped: Workspace is in Monocle mode' >/dev/null 2>&1")
+ os.exit(0)
+end
+
+local clients = exec_json("hyprctl -j clients") or {}
+local monitors = exec_json("hyprctl -j monitors") or {}
+
+-- 3. Filter clients on the active workspace
+local ws_clients = {}
+for _, client in ipairs(clients) do
+ if client.workspace and client.workspace.id == active_ws.id and not client.hidden then
+ table.insert(ws_clients, client)
+ end
+end
+
+local window_count = #ws_clients
+if window_count == 0 then
+ os.exit(0)
+end
+
+-- 4. Determine monitor dimensions and usable area (accounting for scaling and reserved bar margins)
+local active_monitor = nil
+for _, m in ipairs(monitors) do
+ if (active_ws.monitor and m.name == active_ws.monitor) or
+ (active_ws.monitorID and m.id == active_ws.monitorID) or
+ m.focused == true then
+ active_monitor = m
+ break
+ end
+end
+
+local mon_width = 1920
+local mon_height = 1080
+local scale = 1.0
+local res_left, res_top, res_right, res_bottom = 0, 0, 0, 0
+
+if active_monitor then
+ mon_width = active_monitor.width or mon_width
+ mon_height = active_monitor.height or mon_height
+ scale = active_monitor.scale or 1.0
+ if scale <= 0 then scale = 1.0 end
+
+ if active_monitor.reserved and type(active_monitor.reserved) == "table" then
+ res_left = active_monitor.reserved[1] or 0
+ res_top = active_monitor.reserved[2] or 0
+ res_right = active_monitor.reserved[3] or 0
+ res_bottom = active_monitor.reserved[4] or 0
+ end
+end
+
+local screen_w = math.floor(mon_width / scale)
+local screen_h = math.floor(mon_height / scale)
+local usable_w = math.max(320, screen_w - (res_left + res_right))
+local usable_h = math.max(240, screen_h - (res_top + res_bottom))
+
+-- 5. Calculate dynamic uniform window size based on window count and screen resolution
+local width_ratio
+local height_ratio
+
+if window_count == 1 then
+ width_ratio = 0.75
+ height_ratio = 0.75
+elseif window_count == 2 then
+ width_ratio = 0.58
+ height_ratio = 0.65
+elseif window_count <= 4 then
+ width_ratio = 0.48
+ height_ratio = 0.50
+elseif window_count <= 6 then
+ width_ratio = 0.40
+ height_ratio = 0.42
+else
+ width_ratio = math.max(0.30, 0.40 - (window_count - 6) * 0.015)
+ height_ratio = math.max(0.30, 0.42 - (window_count - 6) * 0.015)
+end
+
+local min_w = math.min(usable_w, 480)
+local min_h = math.min(usable_h, 320)
+local target_w = math.floor(math.max(min_w, math.min(usable_w, usable_w * width_ratio)))
+local target_h = math.floor(math.max(min_h, math.min(usable_h, usable_h * height_ratio)))
+
+-- 6. Build and execute atomic Hyprland batch command
+local batch_commands = {}
+
+for _, client in ipairs(ws_clients) do
+ local address = "address:" .. client.address
+
+ -- Ensure window is floating
+ table.insert(batch_commands, string.format("dispatch setfloating %s", address))
+
+ -- Resize to computed uniform dimensions
+ table.insert(
+ batch_commands,
+ string.format("dispatch resizewindowpixel exact %d %d,%s", target_w, target_h, address)
+ )
+
+ -- Center window
+ table.insert(batch_commands, string.format("dispatch centerwindow %s", address))
+end
+
+if #batch_commands > 0 then
+ local full_cmd = string.format("hyprctl --batch '%s'", table.concat(batch_commands, " ; "))
+ os.execute(full_cmd)
+end
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage