119789cc53
brightnessctl -m s sets and prints the new percentage in one invocation, so drop the second brightnessctl call per (repeating) brightness keypress.
473 lines
24 KiB
Cheetah
473 lines
24 KiB
Cheetah
local mainMod = "SUPER"
|
|
|
|
-- Apps
|
|
local terminal = "uwsm app -- kitty"
|
|
local term_tmux = "uwsm app -- kitty -e tmux"
|
|
local term_tmux_append = "uwsm app -- kitty -e tmux a"
|
|
local editor = "uwsm app -- kitty -e nvim"
|
|
local alteditor = "uwsm app -- zeditor"
|
|
local filemanager = "uwsm app -- nautilus"
|
|
local launcher = "uwsm app -- owlry -p app,cmd,system,ssh"
|
|
local clipman = "uwsm app -- owlry -m clipboard"
|
|
local browser = "uwsm app -- firefox"
|
|
local browserprv = "uwsm app -- firefox --private-window"
|
|
local browsernewinst = "uwsm app -- firefox --new-instance"
|
|
local altbrowser = "uwsm app -- chromium"
|
|
local taskman = "uwsm app -- owlry -m uuctl"
|
|
local pwdmgr = "uwsm app -- bitwarden-desktop"
|
|
local soundctl = "uwsm app -- pwvucontrol"
|
|
|
|
local notcenter = "uwsm app -- swaync-client -t -sw"
|
|
local notdnd = "uwsm app -- swaync-client -d"
|
|
local nothide = "uwsm app -- swaync-client --hide-latest"
|
|
|
|
-- First-class launchers
|
|
hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(terminal), { description = "Terminal" })
|
|
hl.bind(mainMod .. " + SHIFT + Return", hl.dsp.exec_cmd(term_tmux), { description = "Terminal (tmux)" })
|
|
hl.bind(mainMod .. " + CTRL + Return", hl.dsp.exec_cmd(term_tmux_append), { description = "Terminal (attach tmux)" })
|
|
hl.bind(mainMod .. " + F1", hl.dsp.exec_cmd("qs ipc call keybinds toggle"), { description = "Show keybinds" })
|
|
hl.bind(mainMod .. " + ALT + F1", hl.dsp.exec_cmd("hypr-show-binds"), { description = "Show raw binds (owlry)" })
|
|
hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(filemanager), { description = "File manager" })
|
|
hl.bind(mainMod .. " + W", hl.dsp.exec_cmd(browser), { description = "Browser" })
|
|
hl.bind(mainMod .. " + SHIFT + W", hl.dsp.exec_cmd(browserprv), { description = "Browser (private)" })
|
|
hl.bind(mainMod .. " + CTRL + W", hl.dsp.exec_cmd(altbrowser), { description = "Chromium" })
|
|
hl.bind(mainMod .. " + ALT + W", hl.dsp.exec_cmd(browsernewinst),{ description = "Firefox (new instance)" })
|
|
hl.bind(mainMod .. " + Space", hl.dsp.exec_cmd(launcher), { description = "App launcher" })
|
|
|
|
-- Secondary launchers
|
|
hl.bind(mainMod .. " + SHIFT + E", hl.dsp.exec_cmd(editor), { description = "Neovim" })
|
|
hl.bind(mainMod .. " + CTRL + E", hl.dsp.exec_cmd(alteditor), { description = "Zed editor" })
|
|
hl.bind(mainMod .. " + X", hl.dsp.exec_cmd(taskman), { description = "Task manager" })
|
|
hl.bind(mainMod .. " + C", hl.dsp.exec_cmd(clipman), { description = "Clipboard history" })
|
|
hl.bind(mainMod .. " + F4", hl.dsp.exec_cmd(soundctl), { description = "Audio mixer" })
|
|
|
|
-- Quick Workspaces submap
|
|
hl.bind(mainMod .. " + A", hl.dsp.submap("quickws"), { description = "Quick workspace jump" })
|
|
hl.define_submap("quickws", function()
|
|
hl.bind("z", function() hl.dispatch(hl.dsp.focus({ workspace = 1 })); hl.dispatch(hl.dsp.submap("reset")) end)
|
|
hl.bind("d", function() hl.dispatch(hl.dsp.focus({ workspace = 2 })); hl.dispatch(hl.dsp.submap("reset")) end)
|
|
hl.bind("a", function() hl.dispatch(hl.dsp.focus({ workspace = 3 })); hl.dispatch(hl.dsp.submap("reset")) end)
|
|
hl.bind("x", function() hl.dispatch(hl.dsp.focus({ workspace = 4 })); hl.dispatch(hl.dsp.submap("reset")) end)
|
|
hl.bind("s", function() hl.dispatch(hl.dsp.focus({ workspace = 5 })); hl.dispatch(hl.dsp.submap("reset")) end)
|
|
hl.bind("c", function() hl.dispatch(hl.dsp.focus({ workspace = 6 })); hl.dispatch(hl.dsp.submap("reset")) end)
|
|
hl.bind("Escape", hl.dsp.submap("reset"))
|
|
hl.bind("Return", hl.dsp.submap("reset"))
|
|
end)
|
|
|
|
-- Notifications
|
|
hl.bind(mainMod .. " + grave", hl.dsp.exec_cmd(notcenter), { description = "Notification center" })
|
|
hl.bind(mainMod .. " + SHIFT + grave", hl.dsp.exec_cmd(notdnd), { description = "Toggle do-not-disturb" })
|
|
hl.bind(mainMod .. " + CTRL + grave", hl.dsp.exec_cmd(nothide), { description = "Hide latest notification" })
|
|
|
|
-- Session
|
|
hl.bind(mainMod .. " + Pause", hl.dsp.exec_cmd("hyprlock"), { description = "Lock screen" })
|
|
hl.bind(mainMod .. " + SHIFT + Pause", hl.dsp.exec_cmd("owlry-power-menu"), { description = "Power menu" })
|
|
hl.bind(mainMod .. " + End", hl.dsp.exec_cmd("hyprlock"), { description = "Lock screen" })
|
|
hl.bind(mainMod .. " + SHIFT + End", hl.dsp.exec_cmd("owlry-power-menu"), { description = "Power menu" })
|
|
|
|
-- Window management
|
|
hl.bind(mainMod .. " + Q", hl.dsp.window.close(), { description = "Close window" })
|
|
hl.bind(mainMod .. " + SHIFT + Q", hl.dsp.window.kill(), { description = "Kill window" })
|
|
hl.bind(mainMod .. " + F", hl.dsp.window.float(), { description = "Toggle float" })
|
|
hl.bind(mainMod .. " + SHIFT + F", hl.dsp.window.fullscreen({ action = "toggle" }), { description = "Toggle fullscreen" })
|
|
hl.bind(mainMod .. " + P", hl.dsp.window.pin(), { description = "Pin window" })
|
|
hl.bind(mainMod .. " + U", hl.dsp.focus({ urgent_or_last = true }), { description = "Focus urgent / last" })
|
|
hl.bind(mainMod .. " + V", hl.dsp.window.center(), { description = "Center window" })
|
|
|
|
-- Special workspaces
|
|
hl.bind(mainMod .. " + SHIFT + Space", hl.dsp.workspace.toggle_special(), { description = "Toggle scratchpad" })
|
|
hl.bind(mainMod .. " + CTRL + Space", hl.dsp.window.move({ workspace = "special" }), { description = "Move to scratchpad" })
|
|
hl.bind(mainMod .. " + N", hl.dsp.workspace.toggle_special("passwordmgr"), { description = "Password manager scratchpad" })
|
|
|
|
-- Monitor focus
|
|
hl.bind(mainMod .. " + I", hl.dsp.focus({ monitor = "l" }), { description = "Focus left monitor" })
|
|
hl.bind(mainMod .. " + O", hl.dsp.focus({ monitor = "r" }), { description = "Focus right monitor" })
|
|
hl.bind(mainMod .. " + SHIFT + I", hl.dsp.workspace.move({ monitor = "l" }), { description = "Move workspace to left monitor" })
|
|
hl.bind(mainMod .. " + SHIFT + O", hl.dsp.workspace.move({ monitor = "r" }), { description = "Move workspace to right monitor" })
|
|
|
|
-- ─── Workspace layout state ───────────────────────────────────────────────────
|
|
-- Shared with layout.lua (the window.active handler reads it for swap layouts).
|
|
local ws_layouts = require("state").ws_layouts
|
|
local ws_cycle_order = {
|
|
"master",
|
|
"lua:master-scroll",
|
|
"lua:slave-master-scroll",
|
|
"lua:center-master-scroll",
|
|
"lua:top-master-scroll",
|
|
"lua:center-master-scroll-v",
|
|
"lua:master-swap",
|
|
"lua:slave-master-swap",
|
|
"lua:top-master-swap",
|
|
"scrolling",
|
|
"monocle",
|
|
}
|
|
|
|
local function active_ws_id()
|
|
local w = hl.get_active_window()
|
|
return w and w.workspace and w.workspace.id
|
|
end
|
|
|
|
local function cur_ws_layout()
|
|
local id = active_ws_id()
|
|
return (id and ws_layouts[id]) or "master"
|
|
end
|
|
|
|
local function set_ws_layout(layout)
|
|
local id = active_ws_id()
|
|
hl.notification.create({ text = "Layout: " .. layout, timeout = 1800, icon = "info" })
|
|
if not id then return end
|
|
ws_layouts[id] = layout
|
|
hl.workspace_rule({ workspace = tostring(id), layout = layout })
|
|
end
|
|
|
|
local function ws_toggle_ms()
|
|
local cur = cur_ws_layout()
|
|
-- Any master variant → scrolling; scrolling/monocle → master
|
|
local next_layout = (cur == "scrolling" or cur == "monocle") and "master" or "scrolling"
|
|
set_ws_layout(next_layout)
|
|
end
|
|
|
|
local function ws_cycle()
|
|
local cur = cur_ws_layout()
|
|
local next_layout = ws_cycle_order[1]
|
|
for i, v in ipairs(ws_cycle_order) do
|
|
if v == cur then
|
|
next_layout = ws_cycle_order[(i % #ws_cycle_order) + 1]
|
|
break
|
|
end
|
|
end
|
|
set_ws_layout(next_layout)
|
|
end
|
|
|
|
local function move_window(dir)
|
|
return function()
|
|
local cur = cur_ws_layout()
|
|
if cur == "scrolling" and (dir == "l" or dir == "r") then
|
|
hl.dispatch(hl.dsp.layout("swapcol " .. dir))
|
|
elseif cur ~= "monocle" then
|
|
hl.dispatch(hl.dsp.window.move({ direction = dir }))
|
|
end
|
|
end
|
|
end
|
|
|
|
local function nav(dir)
|
|
return function()
|
|
local cur = cur_ws_layout()
|
|
if cur == "scrolling" then
|
|
hl.dispatch(hl.dsp.layout("focus " .. dir))
|
|
elseif cur == "master" and dir == "d" then
|
|
hl.dispatch(hl.dsp.layout("addmaster"))
|
|
elseif cur == "master" and dir == "u" then
|
|
hl.dispatch(hl.dsp.layout("removemaster"))
|
|
else
|
|
hl.dispatch(hl.dsp.focus({ direction = dir }))
|
|
end
|
|
end
|
|
end
|
|
|
|
local function resize_h(sign)
|
|
return function()
|
|
local cur = cur_ws_layout()
|
|
if cur == "scrolling" then
|
|
hl.dispatch(hl.dsp.layout("colresize " .. sign .. "0.1"))
|
|
elseif cur ~= "monocle" then
|
|
hl.dispatch(hl.dsp.window.resize({ x = sign == "+" and 25 or -25, y = 0, relative = true }))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Layout
|
|
hl.bind(mainMod .. " + comma", ws_toggle_ms, { description = "Toggle master/scroll layout" })
|
|
hl.bind(mainMod .. " + period", ws_cycle, { description = "Cycle workspace layout" })
|
|
|
|
-- Split ratio (ALT+1-9 = 10%-90%, ALT+0 = 95%)
|
|
-- mfact exact for master/custom layouts, colresize for built-in scrolling
|
|
local mfact_layouts = {
|
|
["master"] = true,
|
|
["lua:master-scroll"] = true,
|
|
["lua:slave-master-scroll"] = true,
|
|
["lua:center-master-scroll"] = true,
|
|
["lua:top-master-scroll"] = true,
|
|
["lua:center-master-scroll-v"] = true,
|
|
["lua:master-swap"] = true,
|
|
["lua:slave-master-swap"] = true,
|
|
["lua:top-master-swap"] = true,
|
|
}
|
|
local function layout_ratio(ratio)
|
|
return function()
|
|
local cur = cur_ws_layout()
|
|
if mfact_layouts[cur] then
|
|
hl.dispatch(hl.dsp.layout("mfact exact " .. ratio))
|
|
elseif cur == "scrolling" then
|
|
hl.dispatch(hl.dsp.layout("colresize " .. ratio))
|
|
end
|
|
end
|
|
end
|
|
local function layout_ratio_delta(delta)
|
|
return function()
|
|
local cur = cur_ws_layout()
|
|
local sign = delta > 0 and "+" or ""
|
|
if mfact_layouts[cur] then
|
|
hl.dispatch(hl.dsp.layout("mfact " .. sign .. delta))
|
|
elseif cur == "scrolling" then
|
|
hl.dispatch(hl.dsp.layout("colresize " .. sign .. delta))
|
|
end
|
|
end
|
|
end
|
|
for i = 1, 9 do
|
|
hl.bind(mainMod .. " + ALT + " .. i, layout_ratio(i / 10), { description = "Split ratio " .. (i * 10) .. "%" })
|
|
end
|
|
hl.bind(mainMod .. " + ALT + 0", layout_ratio(0.95), { description = "Split ratio 95%" })
|
|
hl.bind(mainMod .. " + ALT + comma", layout_ratio_delta(-0.05), { description = "Split ratio -5%" })
|
|
hl.bind(mainMod .. " + ALT + period", layout_ratio_delta(0.05), { description = "Split ratio +5%" })
|
|
|
|
-- Scrolling layout: resize ALL columns
|
|
for i = 1, 9 do
|
|
local ratio = i / 10
|
|
hl.bind(mainMod .. " + CTRL + ALT + " .. i, function()
|
|
if cur_ws_layout() == "scrolling" then
|
|
hl.dispatch(hl.dsp.layout("colresize all " .. ratio))
|
|
end
|
|
end, { description = "All columns " .. (i * 10) .. "% (scrolling)" })
|
|
end
|
|
hl.bind(mainMod .. " + CTRL + ALT + 0", function()
|
|
if cur_ws_layout() == "scrolling" then
|
|
hl.dispatch(hl.dsp.layout("colresize all 0.95"))
|
|
end
|
|
end, { description = "All columns 95% (scrolling)" })
|
|
|
|
-- Smart Gaps Toggle
|
|
hl.bind(mainMod .. " + SHIFT + G", function()
|
|
local gapsIn = hl.get_config("general.gaps_in")
|
|
local current = type(gapsIn) == "table" and (gapsIn.top or 0) or (gapsIn or 0)
|
|
if current ~= 0 then
|
|
hl.config({ general = { gaps_in = 0, gaps_out = 0 } })
|
|
hl.notification.create({ text = "Gaps: OFF", timeout = 2000, icon = "info" })
|
|
else
|
|
hl.config({ general = { gaps_in = 5, gaps_out = 5 } })
|
|
hl.notification.create({ text = "Gaps: ON", timeout = 2000, icon = "ok" })
|
|
end
|
|
end, { description = "Toggle gaps" })
|
|
|
|
-- Vim-like navigation
|
|
hl.bind(mainMod .. " + H", hl.dsp.focus({ direction = "l" }), { description = "Focus left" })
|
|
hl.bind(mainMod .. " + L", hl.dsp.focus({ direction = "r" }), { description = "Focus right" })
|
|
hl.bind(mainMod .. " + K", hl.dsp.focus({ direction = "u" }), { description = "Focus up" })
|
|
hl.bind(mainMod .. " + J", hl.dsp.focus({ direction = "d" }), { description = "Focus down" })
|
|
|
|
-- Move window (layout-aware)
|
|
hl.bind(mainMod .. " + SHIFT + H", move_window("l"), { description = "Move window left" })
|
|
hl.bind(mainMod .. " + SHIFT + L", move_window("r"), { description = "Move window right" })
|
|
hl.bind(mainMod .. " + SHIFT + K", move_window("u"), { description = "Move window up" })
|
|
hl.bind(mainMod .. " + SHIFT + J", move_window("d"), { description = "Move window down" })
|
|
|
|
-- Resize submap
|
|
hl.bind(mainMod .. " + R", hl.dsp.submap("resize"), { description = "Resize mode" })
|
|
hl.define_submap("resize", function()
|
|
hl.bind("h", hl.dsp.window.resize({ x = -25, y = 0, relative = true }), { repeating = true })
|
|
hl.bind("l", hl.dsp.window.resize({ x = 25, y = 0, relative = true }), { repeating = true })
|
|
hl.bind("k", hl.dsp.window.resize({ x = 0, y = -25, relative = true }), { repeating = true })
|
|
hl.bind("j", hl.dsp.window.resize({ x = 0, y = 25, relative = true }), { repeating = true })
|
|
hl.bind("SHIFT + h", hl.dsp.window.resize({ x = -60, y = 0, relative = true }), { repeating = true })
|
|
hl.bind("SHIFT + l", hl.dsp.window.resize({ x = 60, y = 0, relative = true }), { repeating = true })
|
|
hl.bind("SHIFT + k", hl.dsp.window.resize({ x = 0, y = -60, relative = true }), { repeating = true })
|
|
hl.bind("SHIFT + j", hl.dsp.window.resize({ x = 0, y = 60, relative = true }), { repeating = true })
|
|
hl.bind("Escape", hl.dsp.submap("reset"))
|
|
hl.bind("Return", hl.dsp.submap("reset"))
|
|
end)
|
|
|
|
-- Zoom submap
|
|
hl.bind(mainMod .. " + M", hl.dsp.submap("zoom"), { description = "Zoom mode" })
|
|
hl.define_submap("zoom", function()
|
|
hl.bind("equal", hl.dsp.exec_cmd("hypr-zoom-step +0.2"))
|
|
hl.bind("minus", hl.dsp.exec_cmd("hypr-zoom-step -0.2"))
|
|
hl.bind("0", hl.dsp.exec_cmd("hypr-zoom-step reset"))
|
|
hl.bind("Escape", hl.dsp.submap("reset"))
|
|
hl.bind("Return", hl.dsp.submap("reset"))
|
|
end)
|
|
|
|
-- Workspace cycling
|
|
hl.bind(mainMod .. " + Tab", hl.dsp.focus({ workspace = "m+1" }), { description = "Next workspace" })
|
|
hl.bind(mainMod .. " + SHIFT + Tab", hl.dsp.focus({ workspace = "m-1" }), { description = "Previous workspace" })
|
|
|
|
-- Switch Workspaces 1-10 (IDs 21-30 in setup)
|
|
for i = 1, 9 do
|
|
hl.bind(mainMod .. " + " .. i, hl.dsp.focus({ workspace = 20 + i }), { description = "Workspace " .. i })
|
|
hl.bind(mainMod .. " + SHIFT + " .. i, hl.dsp.window.move({ workspace = 20 + i }), { description = "Move to workspace " .. i })
|
|
end
|
|
hl.bind(mainMod .. " + 0", hl.dsp.focus({ workspace = 30 }), { description = "Workspace 10" })
|
|
hl.bind(mainMod .. " + SHIFT + 0", hl.dsp.window.move({ workspace = 30 }), { description = "Move to workspace 10" })
|
|
|
|
-- Groups
|
|
local function smart_group(dir)
|
|
local w = hl.get_active_window()
|
|
local grouped = w and type(w.grouped) == "table" and #w.grouped > 0
|
|
if grouped then
|
|
hl.dispatch(hl.dsp.window.move({ out_of_group = true }))
|
|
else
|
|
hl.dispatch(hl.dsp.window.move({ into_or_create_group = dir }))
|
|
end
|
|
end
|
|
|
|
hl.bind(mainMod .. " + G", hl.dsp.group.toggle(), { description = "Toggle group" })
|
|
hl.bind(mainMod .. " + CTRL + G", hl.dsp.window.move({ out_of_group = true }), { description = "Leave group" })
|
|
hl.bind(mainMod .. " + CTRL + H", hl.dsp.group.prev(), { description = "Group: previous tab" })
|
|
hl.bind(mainMod .. " + CTRL + L", hl.dsp.group.next(), { description = "Group: next tab" })
|
|
hl.bind(mainMod .. " + CTRL + J", hl.dsp.group.move_window({ forward = true }), { description = "Group: move tab forward" })
|
|
hl.bind(mainMod .. " + CTRL + K", hl.dsp.group.move_window({ forward = false }), { description = "Group: move tab back" })
|
|
hl.bind(mainMod .. " + CTRL + SHIFT + H", function() smart_group("l") end, { description = "Smart group left" })
|
|
hl.bind(mainMod .. " + CTRL + SHIFT + J", function() smart_group("d") end, { description = "Smart group down" })
|
|
hl.bind(mainMod .. " + CTRL + SHIFT + K", function() smart_group("u") end, { description = "Smart group up" })
|
|
hl.bind(mainMod .. " + CTRL + SHIFT + L", function() smart_group("r") end, { description = "Smart group right" })
|
|
hl.bind(mainMod .. " + Z", hl.dsp.group.next(), { description = "Group: next tab" })
|
|
hl.bind(mainMod .. " + SHIFT + Z", hl.dsp.group.prev(), { description = "Group: previous tab" })
|
|
|
|
-- Layout-aware navigation
|
|
hl.bind(mainMod .. " + ALT + H", nav("l"), { description = "Nav left (layout-aware)" })
|
|
hl.bind(mainMod .. " + ALT + L", nav("r"), { description = "Nav right (layout-aware)" })
|
|
hl.bind(mainMod .. " + ALT + J", nav("d"), { description = "Nav down / add master" })
|
|
hl.bind(mainMod .. " + ALT + K", nav("u"), { description = "Nav up / remove master" })
|
|
hl.bind(mainMod .. " + ALT + Tab", nav("r"), { description = "Nav right (layout-aware)" })
|
|
hl.bind(mainMod .. " + ALT + SHIFT + Tab", nav("l"), { description = "Nav left (layout-aware)" })
|
|
hl.bind(mainMod .. " + ALT + SHIFT + J", resize_h("-"), { description = "Shrink split" })
|
|
hl.bind(mainMod .. " + ALT + SHIFT + K", resize_h("+"), { description = "Grow split" })
|
|
|
|
-- Mouse binds
|
|
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
|
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
|
|
|
-- master-scroll slave scrolling (mouse wheel + bracket fallback)
|
|
-- Guard by layout name: built-in and swap layouts error on unknown scroll messages.
|
|
local scroll_layouts = {
|
|
["lua:master-scroll"] = true,
|
|
["lua:slave-master-scroll"] = true,
|
|
["lua:center-master-scroll"] = true,
|
|
["lua:top-master-scroll"] = true,
|
|
["lua:center-master-scroll-v"] = true,
|
|
}
|
|
local function layout_scroll(dir)
|
|
return function()
|
|
if scroll_layouts[cur_ws_layout()] then
|
|
hl.dispatch(hl.dsp.layout(dir))
|
|
end
|
|
end
|
|
end
|
|
hl.bind(mainMod .. " + mouse_down", layout_scroll("scrollup"))
|
|
hl.bind(mainMod .. " + mouse_up", layout_scroll("scrolldown"))
|
|
hl.bind(mainMod .. " + bracketright", layout_scroll("scrolldown"), { description = "Scroll slave pane down" })
|
|
hl.bind(mainMod .. " + bracketleft", layout_scroll("scrollup"), { description = "Scroll slave pane up" })
|
|
|
|
-- Screenshots
|
|
hl.bind("Print", hl.dsp.exec_cmd("grimblast --notify copy screen"), { description = "Screenshot: full screen" })
|
|
hl.bind(mainMod .. " + Print", hl.dsp.exec_cmd("owlry-screenshot-menu"), { description = "Screenshot menu" })
|
|
hl.bind("SHIFT + Print", hl.dsp.exec_cmd("uwsm app -- kitty --class=scrrec -e wf-recorder -f ~/Videos/scrrec.mkv -y -g \"$(slurp)\""), { description = "Screen record selection" })
|
|
|
|
-- Multimedia
|
|
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true, description = "Volume up" })
|
|
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { repeating = true, description = "Volume down" })
|
|
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true, description = "Mute" })
|
|
hl.bind("SHIFT + XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%+"), { repeating = true, description = "Mic volume up" })
|
|
hl.bind("SHIFT + XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%-"), { repeating = true, description = "Mic volume down" })
|
|
hl.bind("SHIFT + XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"),{ locked = true, description = "Mic mute" })
|
|
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true, description = "Play/pause" })
|
|
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true, description = "Play/pause" })
|
|
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true, description = "Next track" })
|
|
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true, description = "Previous track" })
|
|
|
|
-- Brightness — let brightnessctl do the math, then push the new percentage to
|
|
-- the Quickshell OSD (field 4 of `-m` output is the percentage, e.g. "70%").
|
|
local function brightness_step(arg)
|
|
-- Single brightnessctl call: `-m s` sets and prints the machine line
|
|
-- (device,class,current,percent,max); field 4 is the new percentage.
|
|
return hl.dsp.exec_cmd(
|
|
"qs ipc call osd brightness \"$(brightnessctl -e4 -n2 -m s " .. arg .. " | cut -d, -f4 | tr -d %)\""
|
|
)
|
|
end
|
|
hl.bind("XF86MonBrightnessUp", brightness_step("1%+"), { locked = true, repeating = true, description = "Brightness up (1%)" })
|
|
hl.bind("XF86MonBrightnessDown", brightness_step("1%-"), { locked = true, repeating = true, description = "Brightness down (1%)" })
|
|
hl.bind("SHIFT + XF86MonBrightnessUp", brightness_step("5%+"), { locked = true, repeating = true, description = "Brightness up (5%)" })
|
|
hl.bind("SHIFT + XF86MonBrightnessDown", brightness_step("5%-"), { locked = true, repeating = true, description = "Brightness down (5%)" })
|
|
hl.bind("CTRL + XF86MonBrightnessUp", brightness_step("70%"), { locked = true, description = "Brightness 70%" })
|
|
hl.bind("CTRL + XF86MonBrightnessDown", brightness_step("20%"), { locked = true, description = "Brightness 20%" })
|
|
|
|
-- Blue light filter manual override
|
|
-- TODO: Temporary workaround until `hyprctl hyprsunset reset` is supported in local packages.
|
|
-- Once supported, replace `get_scheduled_temperature` and `systemctl`/`hyprctl` workaround with `hyprctl hyprsunset reset`.
|
|
local function get_scheduled_temperature()
|
|
local time = os.date("*t")
|
|
local mins = time.hour * 60 + time.min
|
|
-- Profiles matching ~/.config/hypr/hyprsunset.conf:
|
|
-- Day: 07:00 (420 mins) -> identity
|
|
-- Evening: 19:30 (1170 mins) -> 4500
|
|
-- Night: 22:30 (1350 mins) -> 3200
|
|
if mins >= 1350 or mins < 420 then
|
|
return 3200
|
|
elseif mins >= 1170 and mins < 1350 then
|
|
return 4500
|
|
else
|
|
return "identity"
|
|
end
|
|
end
|
|
|
|
local bluelight_enabled = true
|
|
local function toggle_bluelight()
|
|
bluelight_enabled = not bluelight_enabled
|
|
if bluelight_enabled then
|
|
local target = get_scheduled_temperature()
|
|
if target == "identity" then
|
|
hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprsunset identity"))
|
|
hl.notification.create({ text = "Blue Light Filter: Auto (OFF/Day)", timeout = 1500, icon = "info" })
|
|
else
|
|
hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprsunset temperature " .. tostring(target)))
|
|
hl.notification.create({ text = "Blue Light Filter: Auto (" .. tostring(target) .. "K)", timeout = 1500, icon = "ok" })
|
|
end
|
|
else
|
|
hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprsunset identity"))
|
|
hl.notification.create({ text = "Blue Light Filter: OFF", timeout = 1500, icon = "info" })
|
|
end
|
|
end
|
|
hl.bind(mainMod .. " + SHIFT + B", toggle_bluelight, { description = "Toggle blue light filter" })
|
|
|
|
{{- if (index .chezmoi.config.data.tags "laptop") }}
|
|
|
|
-- ─── Surface Type Cover ──────────────────────────────────────────────────────
|
|
local tc_touchpad = "microsoft-surface-type-cover-touchpad"
|
|
local tc_keyboard = "microsoft-surface-type-cover-keyboard"
|
|
|
|
local function device_enabled(name)
|
|
-- Reads live state; falls back to true on any error so toggles still work.
|
|
local ok, v = pcall(hl.get_config, "device[" .. name .. "]:enabled")
|
|
if not ok then return true end
|
|
if v == nil then return true end
|
|
return v ~= false and v ~= 0 and v ~= "false"
|
|
end
|
|
|
|
local function set_device(name, enabled)
|
|
hl.device({ name = name, enabled = enabled })
|
|
end
|
|
|
|
-- SUPER+T: toggle touchpad based on live state (no local-cache desync)
|
|
hl.bind(mainMod .. " + T", function()
|
|
local new_state = not device_enabled(tc_touchpad)
|
|
set_device(tc_touchpad, new_state)
|
|
hl.notification.create({
|
|
text = "Touchpad: " .. (new_state and "ON" or "OFF"),
|
|
timeout = 1500,
|
|
icon = new_state and "ok" or "info",
|
|
})
|
|
end, { description = "Toggle touchpad" })
|
|
|
|
-- Tablet-mode switch: fold cover back → disable kb+touchpad, unfold → re-enable
|
|
hl.bind("switch:on:Microsoft Surface Type Cover Tablet Mode Switch", function()
|
|
set_device(tc_touchpad, false)
|
|
set_device(tc_keyboard, false)
|
|
hl.notification.create({ text = "Tablet mode", timeout = 1500, icon = "info" })
|
|
end, { description = "Tablet mode: disable Type Cover" })
|
|
|
|
hl.bind("switch:off:Microsoft Surface Type Cover Tablet Mode Switch", function()
|
|
set_device(tc_touchpad, true)
|
|
set_device(tc_keyboard, true)
|
|
hl.notification.create({ text = "Laptop mode", timeout = 1500, icon = "ok" })
|
|
end, { description = "Laptop mode: enable Type Cover" })
|
|
{{- end }}
|