Compare commits
2
Commits
049e9a9f5e
...
540baacb15
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
540baacb15 | ||
|
|
99b0075ada |
@@ -0,0 +1,63 @@
|
|||||||
|
-- keybind_registry.lua — wrap hl.bind so every keybind records a runnable
|
||||||
|
-- action alongside its chord/description.
|
||||||
|
--
|
||||||
|
-- Hyprland's Lua API registers binds through hl.bind(keys, dispatcher, opts),
|
||||||
|
-- and `hyprctl binds -j` reports those as `dispatcher: "__lua"` with an opaque
|
||||||
|
-- registry reference — the underlying command is not recoverable from the
|
||||||
|
-- compositor afterwards. This module captures the action at registration time:
|
||||||
|
--
|
||||||
|
-- exec(cmd) -> a tagged placeholder for the exec_cmd dispatcher (the final
|
||||||
|
-- command string is resolved here, so concatenations like
|
||||||
|
-- "hyprshot ... " .. var.screenshot_folder work automatically)
|
||||||
|
-- bind(...) -> same signature/behavior as hl.bind; converts the exec
|
||||||
|
-- placeholder back into a real dispatcher and records the row
|
||||||
|
-- write(path)-> dump the registry as "keys\x1fdescription\x1fcmd" TSV so the
|
||||||
|
-- show-keybinds picker can execute the selected entry
|
||||||
|
--
|
||||||
|
-- Pure dispatcher binds (hl.dsp.window.close(), hl.dsp.focus(...), ...) pass
|
||||||
|
-- through unchanged and are recorded with an empty cmd (informational only).
|
||||||
|
|
||||||
|
local registry = {}
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
--- Tag a shell command so bind() knows to turn it into an exec_cmd dispatcher.
|
||||||
|
function M.exec(cmd)
|
||||||
|
return { __cmd = cmd }
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Register a keybind like hl.bind, but record it in the registry first.
|
||||||
|
--- @param keys string final key string (e.g. "SUPER + Q")
|
||||||
|
--- @param action any M.exec(cmd) or a real dispatcher from hl.dsp.*
|
||||||
|
--- @param opts? table hl.bind options (description, flags, ...)
|
||||||
|
function M.bind(keys, action, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local dsp = action
|
||||||
|
local cmd = ""
|
||||||
|
if type(action) == "table" and action.__cmd ~= nil then
|
||||||
|
cmd = action.__cmd
|
||||||
|
dsp = hl.dsp.exec_cmd(cmd)
|
||||||
|
end
|
||||||
|
table.insert(registry, { keys = keys, description = opts.description or "", cmd = cmd })
|
||||||
|
return hl.bind(keys, dsp, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Dump the registry as "keys\x1fdescription\x1fcmd" lines (one per bind).
|
||||||
|
function M.write(path)
|
||||||
|
local f, err = io.open(path, "w")
|
||||||
|
if not f then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
for _, b in ipairs(registry) do
|
||||||
|
f:write(b.keys, "\x1f", b.description, "\x1f", b.cmd, "\n")
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
return registry
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Access the raw registry rows ({keys, description, cmd}).
|
||||||
|
function M.get()
|
||||||
|
return registry
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -1,135 +1,140 @@
|
|||||||
local var = require("modules.variables")
|
local var = require("modules.variables")
|
||||||
local command = require("modules.shell_commands")
|
local command = require("modules.shell_commands")
|
||||||
local env = require("env")
|
local env = require("env")
|
||||||
|
-- bind/exec wrap hl.bind so each keybind also records a runnable command for
|
||||||
|
-- the show-keybinds picker (see modules/keybind_registry.lua).
|
||||||
|
local reg = require("modules.keybind_registry")
|
||||||
|
local bind = reg.bind
|
||||||
|
local exec = reg.exec
|
||||||
-- See https://wiki.hypr.land/Configuring/Keywords/
|
-- See https://wiki.hypr.land/Configuring/Keywords/
|
||||||
|
|
||||||
-- Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
-- Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
||||||
hl.bind(var.mainMod .. " + RETURN", hl.dsp.exec_cmd(var.terminal), { description = "Open terminal (kitty)" })
|
bind(var.mainMod .. " + RETURN", exec(var.terminal), { description = "Open terminal (kitty)" })
|
||||||
hl.bind(var.mainMod .. " + ALT + RETURN", hl.dsp.exec_cmd(var.terminal_tmux), { description = "Open terminal (kitty)" })
|
bind(var.mainMod .. " + ALT + RETURN", exec(var.terminal_tmux), { description = "Open terminal (kitty)" })
|
||||||
hl.bind(var.mainMod .. " + Q", hl.dsp.window.close(), { description = "Close focused window" })
|
bind(var.mainMod .. " + Q", hl.dsp.window.close(), { description = "Close focused window" })
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + ESCAPE",
|
var.mainMod .. " + ESCAPE",
|
||||||
hl.dsp.exec_cmd(command.session),
|
exec(command.session),
|
||||||
{ description = "Session menu (logout/reboot/shutdown)" }
|
{ description = "Session menu (logout/reboot/shutdown)" }
|
||||||
)
|
)
|
||||||
hl.bind(var.mainMod .. " + SHIFT + ESCAPE", hl.dsp.exec_cmd(command.logout), { description = "Exit Hyprland" })
|
bind(var.mainMod .. " + SHIFT + ESCAPE", exec(command.logout), { description = "Exit Hyprland" })
|
||||||
hl.bind(var.mainMod .. " + CTRL + ESCAPE", hl.dsp.exec_cmd(command.shutdown), { description = "Power off the system" })
|
bind(var.mainMod .. " + CTRL + ESCAPE", exec(command.shutdown), { description = "Power off the system" })
|
||||||
hl.bind(var.mainMod .. " + ALT + ESCAPE", hl.dsp.exec_cmd(command.reboot), { description = "Reboot the system" })
|
bind(var.mainMod .. " + ALT + ESCAPE", exec(command.reboot), { description = "Reboot the system" })
|
||||||
hl.bind(var.mainMod .. " + E", hl.dsp.exec_cmd(var.fileManager), { description = "Open file manager (dolphin)" })
|
bind(var.mainMod .. " + E", exec(var.fileManager), { description = "Open file manager (dolphin)" })
|
||||||
hl.bind(var.mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }), { description = "Toggle floating window" })
|
bind(var.mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }), { description = "Toggle floating window" })
|
||||||
hl.bind(var.mainMod .. " + SPACE", hl.dsp.exec_cmd(command.menu), { description = "Open app launcher" })
|
bind(var.mainMod .. " + SPACE", exec(command.menu), { description = "Open app launcher" })
|
||||||
hl.bind(var.mainMod .. " + P", hl.dsp.window.pseudo(), { description = "Toggle pseudo-tiling" })
|
bind(var.mainMod .. " + P", hl.dsp.window.pseudo(), { description = "Toggle pseudo-tiling" })
|
||||||
hl.bind(var.mainMod .. " + SHIFT + J", hl.dsp.layout("togglesplit"), { description = "Toggle split layout" })
|
bind(var.mainMod .. " + SHIFT + J", hl.dsp.layout("togglesplit"), { description = "Toggle split layout" })
|
||||||
hl.bind(var.mainMod .. " + I", hl.dsp.exec_cmd("firefox &"), { description = "Open Firefox" })
|
bind(var.mainMod .. " + I", exec("firefox &"), { description = "Open Firefox" })
|
||||||
hl.bind(var.mainMod .. " + n", hl.dsp.exec_cmd("nm-connection-editor"), { description = "Network manager" })
|
bind(var.mainMod .. " + n", exec("nm-connection-editor"), { description = "Network manager" })
|
||||||
hl.bind(var.mainMod .. " + comma", hl.dsp.exec_cmd(command.settings), { description = "settings menu" })
|
bind(var.mainMod .. " + comma", exec(command.settings), { description = "settings menu" })
|
||||||
hl.bind(var.mainMod .. " + S", hl.dsp.exec_cmd(command.control_center), { description = "Control center" })
|
bind(var.mainMod .. " + S", exec(command.control_center), { description = "Control center" })
|
||||||
|
|
||||||
-- Move focus with mainMod or Vim-style
|
-- Move focus with mainMod or Vim-style
|
||||||
hl.bind(var.mainMod .. " + CTRL + h", hl.dsp.focus({ direction = "left" }), { description = "Focus window left" })
|
bind(var.mainMod .. " + CTRL + h", hl.dsp.focus({ direction = "left" }), { description = "Focus window left" })
|
||||||
hl.bind(var.mainMod .. " + CTRL + l", hl.dsp.focus({ direction = "right" }), { description = "Focus window right" })
|
bind(var.mainMod .. " + CTRL + l", hl.dsp.focus({ direction = "right" }), { description = "Focus window right" })
|
||||||
hl.bind(var.mainMod .. " + CTRL + k", hl.dsp.focus({ direction = "up" }), { description = "Focus window up" })
|
bind(var.mainMod .. " + CTRL + k", hl.dsp.focus({ direction = "up" }), { description = "Focus window up" })
|
||||||
hl.bind(var.mainMod .. " + CTRL + j", hl.dsp.focus({ direction = "down" }), { description = "Focus window down" })
|
bind(var.mainMod .. " + CTRL + j", hl.dsp.focus({ direction = "down" }), { description = "Focus window down" })
|
||||||
|
|
||||||
-- Move windows with mainMod or Vim-style
|
-- Move windows with mainMod or Vim-style
|
||||||
hl.bind(var.mainMod .. " + SHIFT + h", hl.dsp.window.move({ direction = "left" }), { description = "Move window left" })
|
bind(var.mainMod .. " + SHIFT + h", hl.dsp.window.move({ direction = "left" }), { description = "Move window left" })
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + l",
|
var.mainMod .. " + SHIFT + l",
|
||||||
hl.dsp.window.move({ direction = "right" }),
|
hl.dsp.window.move({ direction = "right" }),
|
||||||
{ description = "Move window right" }
|
{ description = "Move window right" }
|
||||||
)
|
)
|
||||||
hl.bind(var.mainMod .. " + SHIFT + k", hl.dsp.window.move({ direction = "up" }), { description = "Move window up" })
|
bind(var.mainMod .. " + SHIFT + k", hl.dsp.window.move({ direction = "up" }), { description = "Move window up" })
|
||||||
hl.bind(var.mainMod .. " + SHIFT + j", hl.dsp.window.move({ direction = "down" }), { description = "Move window down" })
|
bind(var.mainMod .. " + SHIFT + j", hl.dsp.window.move({ direction = "down" }), { description = "Move window down" })
|
||||||
|
|
||||||
-- Resize focused window with mainMod + Arrows
|
-- Resize focused window with mainMod + Arrows
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + left",
|
var.mainMod .. " + left",
|
||||||
hl.dsp.window.resize({ x = -10, y = 0, relative = true }),
|
hl.dsp.window.resize({ x = -10, y = 0, relative = true }),
|
||||||
{ description = "Resize window left" }
|
{ description = "Resize window left" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + right",
|
var.mainMod .. " + right",
|
||||||
hl.dsp.window.resize({ x = 10, y = 0, relative = true }),
|
hl.dsp.window.resize({ x = 10, y = 0, relative = true }),
|
||||||
{ description = "Resize window right" }
|
{ description = "Resize window right" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + up",
|
var.mainMod .. " + up",
|
||||||
hl.dsp.window.resize({ x = 0, y = -10, relative = true }),
|
hl.dsp.window.resize({ x = 0, y = -10, relative = true }),
|
||||||
{ description = "Resize window up" }
|
{ description = "Resize window up" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + down",
|
var.mainMod .. " + down",
|
||||||
hl.dsp.window.resize({ x = 0, y = 10, relative = true }),
|
hl.dsp.window.resize({ x = 0, y = 10, relative = true }),
|
||||||
{ description = "Resize window down" }
|
{ description = "Resize window down" }
|
||||||
)
|
)
|
||||||
|
|
||||||
-- Switch workspaces with mainMod + [0-9]
|
-- Switch workspaces with mainMod + [0-9]
|
||||||
hl.bind(var.mainMod .. " + 1", hl.dsp.focus({ workspace = 1 }), { description = "Switch to workspace 1" })
|
bind(var.mainMod .. " + 1", hl.dsp.focus({ workspace = 1 }), { description = "Switch to workspace 1" })
|
||||||
hl.bind(var.mainMod .. " + 2", hl.dsp.focus({ workspace = 2 }), { description = "Switch to workspace 2" })
|
bind(var.mainMod .. " + 2", hl.dsp.focus({ workspace = 2 }), { description = "Switch to workspace 2" })
|
||||||
hl.bind(var.mainMod .. " + 3", hl.dsp.focus({ workspace = 3 }), { description = "Switch to workspace 3" })
|
bind(var.mainMod .. " + 3", hl.dsp.focus({ workspace = 3 }), { description = "Switch to workspace 3" })
|
||||||
hl.bind(var.mainMod .. " + 4", hl.dsp.focus({ workspace = 4 }), { description = "Switch to workspace 4" })
|
bind(var.mainMod .. " + 4", hl.dsp.focus({ workspace = 4 }), { description = "Switch to workspace 4" })
|
||||||
hl.bind(var.mainMod .. " + 5", hl.dsp.focus({ workspace = 5 }), { description = "Switch to workspace 5" })
|
bind(var.mainMod .. " + 5", hl.dsp.focus({ workspace = 5 }), { description = "Switch to workspace 5" })
|
||||||
hl.bind(var.mainMod .. " + 6", hl.dsp.focus({ workspace = 6 }), { description = "Switch to workspace 6" })
|
bind(var.mainMod .. " + 6", hl.dsp.focus({ workspace = 6 }), { description = "Switch to workspace 6" })
|
||||||
hl.bind(var.mainMod .. " + 7", hl.dsp.focus({ workspace = 7 }), { description = "Switch to workspace 7" })
|
bind(var.mainMod .. " + 7", hl.dsp.focus({ workspace = 7 }), { description = "Switch to workspace 7" })
|
||||||
hl.bind(var.mainMod .. " + 8", hl.dsp.focus({ workspace = 8 }), { description = "Switch to workspace 8" })
|
bind(var.mainMod .. " + 8", hl.dsp.focus({ workspace = 8 }), { description = "Switch to workspace 8" })
|
||||||
hl.bind(var.mainMod .. " + 9", hl.dsp.focus({ workspace = 9 }), { description = "Switch to workspace 9" })
|
bind(var.mainMod .. " + 9", hl.dsp.focus({ workspace = 9 }), { description = "Switch to workspace 9" })
|
||||||
hl.bind(var.mainMod .. " + 0", hl.dsp.focus({ workspace = 10 }), { description = "Switch to workspace 10" })
|
bind(var.mainMod .. " + 0", hl.dsp.focus({ workspace = 10 }), { description = "Switch to workspace 10" })
|
||||||
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"), {
|
bind("XF86AudioRaiseVolume", exec("wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
description = "Volume up",
|
description = "Volume up",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
|
bind("XF86AudioLowerVolume", exec("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Volume down",
|
description = "Volume down",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
|
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 1",
|
var.mainMod .. " + SHIFT + 1",
|
||||||
hl.dsp.window.move({ workspace = 1 }),
|
hl.dsp.window.move({ workspace = 1 }),
|
||||||
{ description = "Move window to workspace 1" }
|
{ description = "Move window to workspace 1" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 2",
|
var.mainMod .. " + SHIFT + 2",
|
||||||
hl.dsp.window.move({ workspace = 2 }),
|
hl.dsp.window.move({ workspace = 2 }),
|
||||||
{ description = "Move window to workspace 2" }
|
{ description = "Move window to workspace 2" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 3",
|
var.mainMod .. " + SHIFT + 3",
|
||||||
hl.dsp.window.move({ workspace = 3 }),
|
hl.dsp.window.move({ workspace = 3 }),
|
||||||
{ description = "Move window to workspace 3" }
|
{ description = "Move window to workspace 3" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 4",
|
var.mainMod .. " + SHIFT + 4",
|
||||||
hl.dsp.window.move({ workspace = 4 }),
|
hl.dsp.window.move({ workspace = 4 }),
|
||||||
{ description = "Move window to workspace 4" }
|
{ description = "Move window to workspace 4" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 5",
|
var.mainMod .. " + SHIFT + 5",
|
||||||
hl.dsp.window.move({ workspace = 5 }),
|
hl.dsp.window.move({ workspace = 5 }),
|
||||||
{ description = "Move window to workspace 5" }
|
{ description = "Move window to workspace 5" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 6",
|
var.mainMod .. " + SHIFT + 6",
|
||||||
hl.dsp.window.move({ workspace = 6 }),
|
hl.dsp.window.move({ workspace = 6 }),
|
||||||
{ description = "Move window to workspace 6" }
|
{ description = "Move window to workspace 6" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 7",
|
var.mainMod .. " + SHIFT + 7",
|
||||||
hl.dsp.window.move({ workspace = 7 }),
|
hl.dsp.window.move({ workspace = 7 }),
|
||||||
{ description = "Move window to workspace 7" }
|
{ description = "Move window to workspace 7" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 8",
|
var.mainMod .. " + SHIFT + 8",
|
||||||
hl.dsp.window.move({ workspace = 8 }),
|
hl.dsp.window.move({ workspace = 8 }),
|
||||||
{ description = "Move window to workspace 8" }
|
{ description = "Move window to workspace 8" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 9",
|
var.mainMod .. " + SHIFT + 9",
|
||||||
hl.dsp.window.move({ workspace = 9 }),
|
hl.dsp.window.move({ workspace = 9 }),
|
||||||
{ description = "Move window to workspace 9" }
|
{ description = "Move window to workspace 9" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + SHIFT + 0",
|
var.mainMod .. " + SHIFT + 0",
|
||||||
hl.dsp.window.move({ workspace = 10 }),
|
hl.dsp.window.move({ workspace = 10 }),
|
||||||
{ description = "Move window to workspace 10" }
|
{ description = "Move window to workspace 10" }
|
||||||
@@ -142,100 +147,105 @@ hl.bind(
|
|||||||
-- bind = $mainMod SHIFT, S, movetoworkspace, special:magic
|
-- bind = $mainMod SHIFT, S, movetoworkspace, special:magic
|
||||||
|
|
||||||
-- Scroll through existing workspaces with mainMod + scroll
|
-- Scroll through existing workspaces with mainMod + scroll
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + mouse_down",
|
var.mainMod .. " + mouse_down",
|
||||||
hl.dsp.focus({ workspace = "e+1" }),
|
hl.dsp.focus({ workspace = "e+1" }),
|
||||||
{ description = "Next workspace (scroll down)" }
|
{ description = "Next workspace (scroll down)" }
|
||||||
)
|
)
|
||||||
hl.bind(
|
bind(
|
||||||
var.mainMod .. " + mouse_up",
|
var.mainMod .. " + mouse_up",
|
||||||
hl.dsp.focus({ workspace = "e-1" }),
|
hl.dsp.focus({ workspace = "e-1" }),
|
||||||
{ description = "Previous workspace (scroll up)" }
|
{ description = "Previous workspace (scroll up)" }
|
||||||
)
|
)
|
||||||
|
|
||||||
-- Move/resize windows with mainMod + LMB/RMB and dragging
|
-- Move/resize windows with mainMod + LMB/RMB and dragging
|
||||||
hl.bind(var.mainMod .. " + mouse:272", hl.dsp.window.drag(), {
|
bind(var.mainMod .. " + mouse:272", hl.dsp.window.drag(), {
|
||||||
mouse = true,
|
mouse = true,
|
||||||
description = "Move window with mouse",
|
description = "Move window with mouse",
|
||||||
})
|
})
|
||||||
hl.bind(var.mainMod .. " + mouse:273", hl.dsp.window.resize(), {
|
bind(var.mainMod .. " + mouse:273", hl.dsp.window.resize(), {
|
||||||
mouse = true,
|
mouse = true,
|
||||||
description = "Resize window with mouse",
|
description = "Resize window with mouse",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Laptop multimedia keys for volume and LCD brightness
|
-- Laptop multimedia keys for volume and LCD brightness
|
||||||
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), {
|
bind("XF86AudioRaiseVolume", exec("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Volume up (repeat)",
|
description = "Volume up (repeat)",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
|
bind("XF86AudioLowerVolume", exec("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Volume down (repeat)",
|
description = "Volume down (repeat)",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), {
|
bind("XF86AudioMute", exec("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Mute audio",
|
description = "Mute audio",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), {
|
bind("XF86AudioMicMute", exec("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Mute microphone",
|
description = "Mute microphone",
|
||||||
})
|
})
|
||||||
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%+"), {
|
bind("XF86MonBrightnessUp", exec("brightnessctl -e4 -n2 set 5%+"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Brightness up",
|
description = "Brightness up",
|
||||||
})
|
})
|
||||||
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%-"), {
|
bind("XF86MonBrightnessDown", exec("brightnessctl -e4 -n2 set 5%-"), {
|
||||||
repeating = true,
|
repeating = true,
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Brightness down",
|
description = "Brightness down",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Requires playerctl
|
-- Requires playerctl
|
||||||
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), {
|
bind("XF86AudioNext", exec("playerctl next"), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Next track",
|
description = "Next track",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), {
|
bind("XF86AudioPause", exec("playerctl play-pause"), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Play/Pause",
|
description = "Play/Pause",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), {
|
bind("XF86AudioPlay", exec("playerctl play-pause"), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Play/Pause",
|
description = "Play/Pause",
|
||||||
})
|
})
|
||||||
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), {
|
bind("XF86AudioPrev", exec("playerctl previous"), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Previous track",
|
description = "Previous track",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Screenshots
|
-- Screenshots
|
||||||
hl.bind("PRINT", hl.dsp.exec_cmd("hyprshot -m region --output-folder " .. var.screenshot_folder), {
|
bind("PRINT", exec("hyprshot -m region --output-folder " .. var.screenshot_folder), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Screenshot region",
|
description = "Screenshot region",
|
||||||
})
|
})
|
||||||
hl.bind("CTRL + PRINT", hl.dsp.exec_cmd("hyprshot -m window --output-folder " .. var.screenshot_folder), {
|
bind("CTRL + PRINT", exec("hyprshot -m window --output-folder " .. var.screenshot_folder), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Screenshot window",
|
description = "Screenshot window",
|
||||||
})
|
})
|
||||||
hl.bind("SHIFT + PRINT", hl.dsp.exec_cmd("hyprshot -m active --output-folder " .. var.screenshot_folder), {
|
bind("SHIFT + PRINT", exec("hyprshot -m active --output-folder " .. var.screenshot_folder), {
|
||||||
locked = true,
|
locked = true,
|
||||||
description = "Screenshot active monitor",
|
description = "Screenshot active monitor",
|
||||||
})
|
})
|
||||||
|
|
||||||
hl.bind(var.mainMod .. " + L", hl.dsp.exec_cmd(command.lock), { locked = true, description = "Lock screen" })
|
bind(var.mainMod .. " + L", exec(command.lock), { locked = true, description = "Lock screen" })
|
||||||
hl.bind(var.mainMod .. " + SHIFT + L", hl.dsp.exec_cmd(command.screensaver), { description = "Start screensaver (any key quits)" })
|
bind(var.mainMod .. " + SHIFT + L", exec(command.screensaver), { description = "Start screensaver (any key quits)" })
|
||||||
hl.bind(var.mainMod .. " + K", hl.dsp.exec_cmd(command.keybinds_show), { description = "Show keybind overview" })
|
bind(var.mainMod .. " + K", exec(command.keybinds_show), { description = "Show keybind overview" })
|
||||||
hl.bind(var.mainMod .. " + W", hl.dsp.exec_cmd(command.wallpaper_select), { description = "Wallpaper select" })
|
bind(var.mainMod .. " + W", exec(command.wallpaper_select), { description = "Wallpaper select" })
|
||||||
|
|
||||||
-- Caelestia extras
|
-- Caelestia extras
|
||||||
if env.shell == "caelestia" then
|
if env.shell == "caelestia" then
|
||||||
hl.bind(var.mainMod .. " + SHIFT + V", hl.dsp.exec_cmd(command.clipboard), { description = "Clipboard history" })
|
bind(var.mainMod .. " + SHIFT + V", exec(command.clipboard), { description = "Clipboard history" })
|
||||||
hl.bind(var.mainMod .. " + period", hl.dsp.exec_cmd(command.emoji), { description = "Emoji picker" })
|
bind(var.mainMod .. " + period", exec(command.emoji), { description = "Emoji picker" })
|
||||||
hl.bind(var.mainMod .. " + SHIFT + K", hl.dsp.exec_cmd(command.show_all), { description = "Toggle utilities" })
|
bind(var.mainMod .. " + SHIFT + K", exec(command.show_all), { description = "Toggle utilities" })
|
||||||
hl.bind("CTRL + ALT + C", hl.dsp.exec_cmd(command.clear_notifications), { description = "Clear notifications" })
|
bind("CTRL + ALT + C", exec(command.clear_notifications), { description = "Clear notifications" })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Persist the registry for the show-keybinds picker
|
||||||
|
-- (scripts/show-keybinds.sh reads this TSV to resolve selected commands).
|
||||||
|
-- Runs on every config load/reload, keeping the picker in sync with the binds.
|
||||||
|
reg.write("/tmp/hypr-keybinds.tsv")
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
-- See https://wiki.hypr.land/Configuring/Window-Rules/ for more
|
-- See https://wiki.hypr.land/Configuring/Window-Rules/ for more
|
||||||
|
|
||||||
-- See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
|
-- See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
|
||||||
@@ -113,26 +111,8 @@ hl.window_rule({
|
|||||||
float = true,
|
float = true,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- windowrule {
|
hl.window_rule({
|
||||||
|
name = "floating-windows",
|
||||||
-- name = no-rounding-steam-games
|
match = { class = "(imv|.*hyprmod)" },
|
||||||
|
float = true,
|
||||||
-- match:class = ^steam_app_.*$
|
})
|
||||||
|
|
||||||
-- rounding = 0
|
|
||||||
|
|
||||||
-- border_size = 0
|
|
||||||
|
|
||||||
-- }
|
|
||||||
|
|
||||||
-- windowrule {
|
|
||||||
|
|
||||||
-- name = no-rounding-gamescope
|
|
||||||
|
|
||||||
-- match:class = ^gamescope$
|
|
||||||
|
|
||||||
-- rounding = 0
|
|
||||||
|
|
||||||
-- border_size = 0
|
|
||||||
|
|
||||||
-- }
|
|
||||||
|
|||||||
@@ -1,48 +1,99 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# show-keybinds.sh — floating keybind overview popup.
|
# show-keybinds.sh — fuzzy, executable keybind picker.
|
||||||
#
|
#
|
||||||
# Reads the live keybind list from `hyprctl binds -j` and shows it in a
|
# SUPER+K opens a floating kitty (class `keybinds`, see
|
||||||
# floating kitty window (single column, sorted A-Z, searchable via less).
|
# windows_and_workspaces.lua) running fzf over the keybind list:
|
||||||
# Display-only: wofi/rofi are gone (the Noctalia launcher has no stdin-list
|
# - start typing immediately: the fzf prompt is focused on open
|
||||||
# mode), so picking a row to execute is not wired up.
|
# - arrow keys browse the (filtered) list
|
||||||
|
# - Enter executes the selected entry's command, then the picker closes
|
||||||
|
# - Esc closes without action
|
||||||
#
|
#
|
||||||
# Note: this config uses the Lua API (hl.bind), so `hyprctl binds -j` reports
|
# Display data is read live from `hyprctl binds -j`. The command behind each
|
||||||
# key + modmask + an opaque Lua registry reference instead of a readable
|
# entry cannot come from hyprctl — Lua-registered binds report as opaque
|
||||||
# dispatcher. The human-readable action comes from the `description` option
|
# `__lua` dispatchers there — so modules/keybind_registry.lua records every
|
||||||
# added to each hl.bind(...) call - keep those descriptions up to date when
|
# command at bind time and writes /tmp/hypr-keybinds.tsv on config load
|
||||||
# you change a bind.
|
# ("keys\x1fdescription\x1fcmd" lines). Entries without a recorded command
|
||||||
|
# (pure dispatchers like window.close) are informational only: Enter closes.
|
||||||
#
|
#
|
||||||
# Usage: show-keybinds.sh [--print]
|
# Usage: show-keybinds.sh [--print | --internal-pick FILE]
|
||||||
# --print print the formatted list to stdout instead of opening kitty
|
# --print print the formatted list to stdout (debug/testing)
|
||||||
# (handy for debugging / testing the formatting pipeline)
|
# --internal-pick F run fzf over picker file F and execute the selection
|
||||||
|
# (spawned by the default mode inside the kitty window)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
readonly SEP=$'\x1f'
|
||||||
# modmask bits reported by hyprctl (XKB modifiers), pairwise bit:name in
|
# modmask bits reported by hyprctl (XKB modifiers), pairwise bit:name in
|
||||||
# display order. See https://wiki.hypr.land/Configuring/Binds/
|
# display order. See https://wiki.hypr.land/Configuring/Binds/
|
||||||
readonly MOD_ORDER=(64 SUPER 8 ALT 4 CTRL 1 SHIFT 2 CAPS 16 MOD2 32 MOD3 128 MOD5)
|
readonly MOD_ORDER=(64 SUPER 8 ALT 4 CTRL 1 SHIFT 2 CAPS 16 MOD2 32 MOD3 128 MOD5)
|
||||||
|
readonly SIDECAR=/tmp/hypr-keybinds.tsv
|
||||||
|
|
||||||
if ! hyprctl binds -j >/dev/null 2>&1; then
|
# --internal-pick mode: runs INSIDE the floating kitty. fzf over the picker
|
||||||
echo "hyprctl binds failed - are you inside a Hyprland session?" >&2
|
# list (lines are "cmd\x1fdisplay"), then execute the chosen command and let
|
||||||
|
# the window close.
|
||||||
|
if [[ "${1:-}" == "--internal-pick" ]]; then
|
||||||
|
pick_file="${2:-}"
|
||||||
|
if [[ -z "$pick_file" || ! -s "$pick_file" ]]; then
|
||||||
|
echo "show-keybinds: --internal-pick needs an existing picker list file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# `|| rc=$?` keeps `set -e` from aborting before cleanup when fzf is
|
||||||
|
# cancelled (e.g. Esc) or filters to no match.
|
||||||
|
rc=0
|
||||||
|
sel="$(fzf --delimiter "$SEP" --with-nth 2.. --cycle --info=inline \
|
||||||
|
--prompt 'keybinds> ' --header 'type to filter · Enter executes · Esc closes' \
|
||||||
|
< "$pick_file")" || rc=$?
|
||||||
|
rm -f "$pick_file"
|
||||||
|
if (( rc != 0 )) || [[ -z "$sel" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
cmd="${sel%%"$SEP"*}"
|
||||||
|
if [[ -n "$cmd" ]]; then
|
||||||
|
# Detach into its own session so the command outlives the picker
|
||||||
|
# window (kitty/pty teardown would otherwise signal its process group).
|
||||||
|
setsid -f sh -c "$cmd" >/dev/null 2>&1 < /dev/null
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Gather rows: prefer the sidecar (has commands); fall back to live `hyprctl
|
||||||
|
# binds -j` (display only, e.g. right after a fresh clone without a reload).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
have_sidecar=0
|
||||||
|
if [[ -s "$SIDECAR" ]]; then
|
||||||
|
have_sidecar=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ! have_sidecar )) && ! hyprctl binds -j >/dev/null 2>&1; then
|
||||||
|
echo "show-keybinds: nothing to show: no keybind registry ($SIDECAR) and no Hyprland session" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rows=()
|
rows=()
|
||||||
|
cmds=()
|
||||||
|
keys=()
|
||||||
|
|
||||||
# modmask\x1fkey\x1fdescription\x1fsubmap\x1farg
|
if (( have_sidecar )); then
|
||||||
# Fields are joined with the unit separator (0x1f): tabs are IFS-whitespace
|
while IFS="$SEP" read -r key description cmd; do
|
||||||
# and `read` would collapse consecutive tabs, eating empty middle fields
|
key="${key//mouse:272/LMB}"
|
||||||
# like the (usually empty) submap. \x1f is not whitespace, so every field
|
key="${key//mouse:273/RMB}"
|
||||||
# keeps its position.
|
if [[ -z "$description" ]]; then
|
||||||
while IFS=$'\x1f' read -r modmask key description submap arg; do
|
description="(no description)"
|
||||||
# Humanize mouse-button binds.
|
fi
|
||||||
|
rows+=("$(printf '%-26s %s' "$key" "$description")")
|
||||||
|
cmds+=("$cmd")
|
||||||
|
keys+=("$key")
|
||||||
|
done < "$SIDECAR"
|
||||||
|
else
|
||||||
|
while IFS="$SEP" read -r modmask key description submap; do
|
||||||
case "$key" in
|
case "$key" in
|
||||||
mouse:272) key="LMB" ;;
|
mouse:272) key="LMB" ;;
|
||||||
mouse:273) key="RMB" ;;
|
mouse:273) key="RMB" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Build the chord, e.g. "SUPER + SHIFT + Q".
|
|
||||||
chord=""
|
chord=""
|
||||||
|
# Rebuild "SUPER + SHIFT + Q" from the modmask bits.
|
||||||
i=0
|
i=0
|
||||||
while (( i < ${#MOD_ORDER[@]} )); do
|
while (( i < ${#MOD_ORDER[@]} )); do
|
||||||
bit=${MOD_ORDER[i]}
|
bit=${MOD_ORDER[i]}
|
||||||
@@ -63,32 +114,48 @@ while IFS=$'\x1f' read -r modmask key description submap arg; do
|
|||||||
else
|
else
|
||||||
rows+=("$(printf '%-26s %s' "$chord" "(no description)")")
|
rows+=("$(printf '%-26s %s' "$chord" "(no description)")")
|
||||||
fi
|
fi
|
||||||
done < <(hyprctl binds -j | jq -r '.[] | [(.modmask | tostring), .key, .description, .submap, .arg] | join("\u001f")')
|
cmds+=("")
|
||||||
|
keys+=("$chord")
|
||||||
|
done < <(hyprctl binds -j | jq -r '.[] | [(.modmask | tostring), .key, .description, .submap] | join("\u001f")')
|
||||||
|
fi
|
||||||
|
|
||||||
if (( ${#rows[@]} == 0 )); then
|
if (( ${#rows[@]} == 0 )); then
|
||||||
echo "No keybinds found." >&2
|
echo "show-keybinds: no keybinds found." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Drop duplicate chords (e.g. media keys bound twice with different flags),
|
# Drop duplicate chords (e.g. media keys bound twice with different flags),
|
||||||
# then sort alphabetically (A-Z).
|
# keeping the first occurrence; carry the matching cmd along. Then sort by
|
||||||
mapfile -t rows < <(printf '%s\n' "${rows[@]}" | awk '{ c = $0; sub(/ {2,}.*/, "", c); if (!seen[c]++) print }' | LC_ALL=C sort -f)
|
# the display column A-Z.
|
||||||
|
declare -A seen=()
|
||||||
|
lines=()
|
||||||
|
for i in "${!rows[@]}"; do
|
||||||
|
if [[ -z "${seen[${keys[$i]}]:-}" ]]; then
|
||||||
|
seen["${keys[$i]}"]=1
|
||||||
|
lines+=("${cmds[$i]}$SEP${rows[$i]}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
mapfile -t lines < <(printf '%s\n' "${lines[@]}" | LC_ALL=C sort -f -t "$SEP" -k2)
|
||||||
|
|
||||||
if [[ "${1:-}" == "--print" ]]; then
|
if [[ "${1:-}" == "--print" ]]; then
|
||||||
printf '%s\n' "${rows[@]}"
|
printf '%s\n' "${lines[@]}" | cut -d "$SEP" -f2-
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Floating display via kitty (class `keybinds` has a float rule in
|
# ---------------------------------------------------------------------------
|
||||||
# hyprland.lua). Rows go through a temp file rather than a stdin pipe so the
|
# Default mode: floating kitty + fzf. Rows go through a temp file; the inner
|
||||||
# popup does not depend on kitty forwarding stdin to its child; the previous
|
# --internal-pick run removes it when done, stale leftovers get cleaned on the
|
||||||
# run's file is cleaned up on the next invocation. q closes the window.
|
# next invocation.
|
||||||
rm -f /tmp/keybinds.??????.txt 2>/dev/null || true
|
# ---------------------------------------------------------------------------
|
||||||
|
rm -f /tmp/keybinds.??????.list 2>/dev/null || true
|
||||||
|
list="$(mktemp /tmp/keybinds.XXXXXX.list)"
|
||||||
|
printf '%s\n' "${lines[@]}" > "$list"
|
||||||
|
|
||||||
if command -v kitty >/dev/null 2>&1; then
|
if command -v kitty >/dev/null 2>&1; then
|
||||||
tmp="$(mktemp /tmp/keybinds.XXXXXX.txt)"
|
kitty --class keybinds --title Keybinds "$0" --internal-pick "$list"
|
||||||
printf '%s\n' "${rows[@]}" > "$tmp"
|
|
||||||
kitty --class keybinds --title Keybinds less -S "$tmp"
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\n' "${rows[@]}"
|
# No kitty: fall back to a plain printout.
|
||||||
|
printf '%s\n' "${lines[@]}" | cut -d "$SEP" -f2-
|
||||||
|
rm -f "$list"
|
||||||
Reference in New Issue
Block a user