To open a new terminal in the focused terminal's directory, the launcher asked Hyprland for the active window with hyprctl, ~10ms on every SUPER+RETURN. The binding now reads the pid from hl.get_active_window() and passes it as --pid. Without one, from the menu or a shell, the launcher asks Hyprland as before. The bind is now a Lua function, which Hyprland reports only as __lua, so o.bind_commands records the command it stands for and the keybindings menu still launches a terminal when the entry is picked. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
226 lines
6.1 KiB
Lua
226 lines
6.1 KiB
Lua
-- Shared helpers for Hyprland Lua configuration.
|
|
|
|
local paths = require("default.hypr.paths")
|
|
|
|
o = o or {}
|
|
|
|
local function shell_quote(value)
|
|
return "'" .. tostring(value):gsub("'", "'\\''") .. "'"
|
|
end
|
|
|
|
o.shell_quote = shell_quote
|
|
|
|
local function file_exists(path)
|
|
local file = io.open(path, "r")
|
|
if file then
|
|
file:close()
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Hyprland reaps its own children, so os.execute() can't retrieve an exit status
|
|
-- from inside the compositor. Read a marker off stdout instead.
|
|
function o.shell_succeeds(command)
|
|
-- Subshell, so the redirection covers every command rather than binding to
|
|
-- the last one and letting an earlier one write its own OK into the pipe.
|
|
local pipe = io.popen("( " .. command .. " ) >/dev/null 2>&1 && echo OK")
|
|
if not pipe then
|
|
return false
|
|
end
|
|
|
|
local output = pipe:read("*a") or ""
|
|
pipe:close()
|
|
|
|
return output:find("OK", 1, true) ~= nil
|
|
end
|
|
|
|
function o.cmd_present(command)
|
|
if command:find("/", 1, true) then
|
|
return file_exists(command)
|
|
end
|
|
|
|
local path = os.getenv("PATH") or "/usr/local/bin:/usr/bin"
|
|
for directory in (path .. ":"):gmatch("([^:]*):") do
|
|
if file_exists((directory ~= "" and directory or ".") .. "/" .. command) then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function o.cmd_missing(command)
|
|
return not o.cmd_present(command)
|
|
end
|
|
|
|
-- The global shortcuts the shell registers, read from the same list it reads.
|
|
local shell_shortcuts = nil
|
|
|
|
local function shell_shortcut_registered(name)
|
|
if not shell_shortcuts then
|
|
shell_shortcuts = {}
|
|
local file = io.open(paths.omarchy_path .. "/default/omarchy/shortcuts", "r")
|
|
if file then
|
|
for line in file:lines() do
|
|
local kind, target = line:match("^(%a+)%s+(%S+)%s*$")
|
|
if kind then
|
|
shell_shortcuts[kind .. "." .. target] = true
|
|
end
|
|
end
|
|
file:close()
|
|
end
|
|
end
|
|
|
|
return shell_shortcuts[name] == true
|
|
end
|
|
|
|
-- Reach the shell through its global shortcut when it registers one, so the
|
|
-- keypress spawns nothing. Anything else runs the command as before.
|
|
local function shell_dispatcher(kind, target, command)
|
|
local name = kind .. "." .. target
|
|
if shell_shortcut_registered(name) then
|
|
return hl.dsp.global("omarchy:" .. name)
|
|
end
|
|
|
|
return command
|
|
end
|
|
|
|
local function command_from(value, description)
|
|
if type(value) ~= "table" then
|
|
return value
|
|
end
|
|
|
|
if value.omarchy then
|
|
return "omarchy-launch-" .. value.omarchy
|
|
elseif value.menu then
|
|
return shell_dispatcher("menu", value.menu, "omarchy-menu toggle " .. shell_quote(value.menu))
|
|
elseif value.panel then
|
|
return shell_dispatcher("panel", value.panel, "omarchy-shell shell toggle " .. shell_quote(value.panel))
|
|
elseif value.audio then
|
|
return shell_dispatcher("audio", value.audio, "omarchy-audio-output-volume " .. shell_quote(value.audio))
|
|
elseif value.brightness then
|
|
local step = value.brightness == "raise" and "+5%" or "5%-"
|
|
return shell_dispatcher("brightness", value.brightness, "omarchy-brightness-display " .. step)
|
|
elseif value.ipc then
|
|
local target, method = value.ipc:match("^([^.]+)%.(.+)$")
|
|
return shell_dispatcher("ipc", value.ipc, "omarchy-shell " .. shell_quote(target) .. " " .. shell_quote(method))
|
|
elseif value.focus and value.launch then
|
|
return o.launch_sole(value.focus, value.launch)
|
|
elseif value.launch then
|
|
return o.launch(value.launch)
|
|
elseif value.webapp then
|
|
if value.focus then
|
|
return o.launch_webapp_sole(description, value.webapp)
|
|
else
|
|
return o.launch_webapp(value.webapp)
|
|
end
|
|
elseif value.tui then
|
|
if value.focus then
|
|
return "omarchy-launch-or-focus-tui " .. shell_quote(value.tui)
|
|
else
|
|
return "omarchy-launch-tui " .. shell_quote(value.tui)
|
|
end
|
|
end
|
|
|
|
return value
|
|
end
|
|
|
|
function o.preinstalled_bindings_enabled()
|
|
if _G.omarchy_preinstalled_bindings ~= nil then
|
|
return _G.omarchy_preinstalled_bindings == true
|
|
end
|
|
|
|
return not file_exists((os.getenv("HOME") or "") .. "/.local/state/omarchy/preinstalls-removed")
|
|
end
|
|
|
|
function o.bind(keys, description, dispatcher, options)
|
|
local opts = options or {}
|
|
|
|
if description then
|
|
opts.description = description
|
|
end
|
|
|
|
dispatcher = command_from(dispatcher, description)
|
|
|
|
if type(dispatcher) == "string" then
|
|
dispatcher = hl.dsp.exec_cmd(dispatcher)
|
|
end
|
|
|
|
hl.bind(keys, dispatcher, opts)
|
|
end
|
|
|
|
function o.rebind(keys, description, dispatcher, options)
|
|
hl.unbind(keys)
|
|
o.bind(keys, description, dispatcher, options)
|
|
end
|
|
|
|
function o.launch(command)
|
|
return "uwsm-app -- " .. command
|
|
end
|
|
|
|
-- The command each function bind stands for, so the keybindings menu can still
|
|
-- run a bind that Hyprland only reports as Lua.
|
|
o.bind_commands = {}
|
|
|
|
-- Hand the launcher the focused window's pid, which it would otherwise ask
|
|
-- Hyprland for, to open the new terminal in that terminal's directory.
|
|
function o.launch_terminal()
|
|
local function launch()
|
|
local window = hl.get_active_window()
|
|
if window and window.pid then
|
|
hl.exec_cmd("omarchy-launch-terminal --pid=" .. window.pid)
|
|
else
|
|
hl.exec_cmd("omarchy-launch-terminal")
|
|
end
|
|
end
|
|
|
|
o.bind_commands[launch] = "omarchy-launch-terminal"
|
|
return launch
|
|
end
|
|
|
|
function o.exec_on_start(command)
|
|
hl.on("hyprland.start", function()
|
|
hl.exec_cmd(command)
|
|
end)
|
|
end
|
|
|
|
function o.launch_on_start(command)
|
|
o.exec_on_start(o.launch(command))
|
|
end
|
|
|
|
function o.launch_webapp(url)
|
|
return "omarchy-launch-webapp " .. shell_quote(url)
|
|
end
|
|
|
|
function o.launch_webapp_sole(name, url)
|
|
return "omarchy-launch-or-focus-webapp " .. shell_quote(name) .. " " .. shell_quote(url)
|
|
end
|
|
|
|
function o.launch_sole(match, command)
|
|
return "omarchy-launch-or-focus " .. shell_quote(match) .. " " .. shell_quote(o.launch(command))
|
|
end
|
|
|
|
function o.bind_toggle(keys, description, toggle, options)
|
|
o.bind(keys, description, "omarchy-toggle-" .. toggle, options)
|
|
end
|
|
|
|
function o.notify(message)
|
|
return "omarchy-notification-send -u low " .. shell_quote(message)
|
|
end
|
|
|
|
function o.window(match, rules)
|
|
rules.match = rules.match or {}
|
|
|
|
if type(match) == "string" then
|
|
rules.match.class = match
|
|
else
|
|
for key, value in pairs(match) do
|
|
rules.match[key] = value
|
|
end
|
|
end
|
|
|
|
hl.window_rule(rules)
|
|
end
|