11e4e94ee8
Lua config (hyprland.d.lua):
- keybinds: layout-aware binds now read the per-workspace layout via
cur_ws_layout() instead of the global hl.get_config("general.layout"),
fixing mouse-wheel/bracket scrolling and ratio keys on the lua:*-scroll
layouts.
- add state.lua shared module (ws_layouts) replacing the _G globals.
- layout: factor the 9 duplicated layout_msg bodies into scroll_msg/swap_msg
builders; drop a dead #state expression.
- rules: NO_BLUELIGHT window.open handler no longer leaks a rule per open
(one per class) and regex-escapes/nil-guards the class.
- monitors: quote non-numeric scale so scale="auto" renders.
- drop debug print() focus handler, local-next shadowing, stray {mouse=true}
on wheel binds (kept on drag/resize, which require it).
Quickshell:
- brightness OSD is now event-driven: Osd.qml gains an IpcHandler(target:osd)
and the keybind pushes the new level via `qs ipc call osd brightness`,
removing the always-on 500ms brightnessctl poll.
- GamemodePill watches GameMode's D-Bus signals via gdbus monitor instead of
polling gamemoded --status every 5s.
Cleanup:
- remove stock hyprland.lua.refactor/ boilerplate and the redundant,
partly-wrong hyprland_lua_api.md (both were deployed into ~/.config/hypr;
.luarc.json already points the LSP at /usr/share/hypr/stubs).
- refresh hypr/AGENTS.md (lua layout) and quickshell/CLAUDE.md (v0.3.0).
127 lines
4.3 KiB
Cheetah
127 lines
4.3 KiB
Cheetah
{{- $tags := .chezmoi.config.data.tags -}}
|
|
|
|
hl.config({
|
|
group = {
|
|
auto_group = true,
|
|
insert_after_current = true,
|
|
focus_removed_window = true,
|
|
drag_into_group = 1,
|
|
merge_groups_on_drag = true,
|
|
merge_groups_on_groupbar = true,
|
|
merge_floated_into_tiled_on_groupbar = true,
|
|
group_on_movetoworkspace = false,
|
|
-- colors to be integrated via theme table
|
|
groupbar = {
|
|
enabled = true,
|
|
height = 16,
|
|
font_family = "GeistMono Nerd Font",
|
|
font_size = 8,
|
|
font_weight_active = "semibold",
|
|
font_weight_inactive = "normal",
|
|
stacked = false,
|
|
gradients = true,
|
|
gradient_rounding = 6,
|
|
gradient_rounding_power = 4.0,
|
|
gradient_round_only_edges = false,
|
|
indicator_height = 0,
|
|
rounding = 0,
|
|
render_titles = true,
|
|
scrolling = true,
|
|
text_padding = 4,
|
|
priority = 3
|
|
}
|
|
}
|
|
})
|
|
|
|
-- Permissions (NEW in v0.55)
|
|
hl.permission({ binary = "/usr/bin/grim", type = "screencopy", mode = "allow" })
|
|
hl.permission({ binary = "/usr/bin/grimblast", type = "screencopy", mode = "allow" })
|
|
hl.permission({ binary = "/usr/bin/wf-recorder", type = "screencopy", mode = "allow" })
|
|
hl.permission({ binary = "/usr/lib/xdg-desktop-portal-hyprland", type = "screencopy", mode = "allow" })
|
|
hl.permission({ binary = "/usr/bin/hyprpm", type = "plugin", mode = "allow" })
|
|
|
|
-- Window Rules
|
|
hl.window_rule({ match = { class = ".*" }, suppress_event = "maximize" })
|
|
|
|
-- XWayland fixes
|
|
hl.window_rule({
|
|
match = { class = "^$", title = "^$", xwayland = true, float = true, fullscreen = false, pin = false },
|
|
no_focus = true
|
|
})
|
|
hl.window_rule({ match = { xwayland = true }, no_initial_focus = true })
|
|
|
|
-- Communication
|
|
hl.window_rule({ match = { class = "^(info\\.mumble\\.Mumble|discord|vesktop|teamspeak-client|TeamSpeak|TeamSpeak 3|teamspeak3)$" }, workspace = "2" })
|
|
hl.window_rule({ match = { class = "^(Element)$" }, workspace = "3" })
|
|
|
|
-- Mail
|
|
hl.window_rule({ match = { class = "^(org\\.mozilla\\.Thunderbird)$" }, workspace = "1" })
|
|
|
|
-- Notes
|
|
hl.window_rule({ match = { class = "^(@joplin/app-desktop)$" }, workspace = "4" })
|
|
|
|
{{- if index $tags "entertainment" }}
|
|
-- Multimedia
|
|
hl.window_rule({ match = { class = "Spotify" }, workspace = "6" })
|
|
{{- end }}
|
|
|
|
{{- if index $tags "cs2" }}
|
|
-- Gaming
|
|
hl.window_rule({ match = { class = "^(steam)$" }, workspace = "5" })
|
|
{{- end }}
|
|
|
|
-- Game Content Bypass (Option A - per-monitor CTM bypass)
|
|
-- Automatically mark Steam games as "game" content type
|
|
hl.window_rule({ match = { class = "^steam_app_%d+$" }, content = "game" })
|
|
|
|
-- Dynamic bypass for custom environment variable NO_BLUELIGHT=1
|
|
local function get_process_env(pid, name)
|
|
if not pid or pid <= 0 then return nil end
|
|
local f = io.open("/proc/" .. tostring(pid) .. "/environ", "r")
|
|
if not f then return nil end
|
|
local content = f:read("*a")
|
|
f:close()
|
|
if not content then return nil end
|
|
for var in content:gmatch("([^%z]+)") do
|
|
local k, v = var:match("^([^=]+)=(.*)$")
|
|
if k == name then
|
|
return v
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Escape regex metacharacters so classes like "org.mozilla.firefox" match literally.
|
|
local function regex_escape(s)
|
|
return (s:gsub("([%^%$%.%|%?%*%+%(%)%[%]%{%}\\])", "\\%1"))
|
|
end
|
|
|
|
-- window_rule registrations persist, so only add one per class (otherwise rules
|
|
-- accumulate on every matching window.open).
|
|
local bluelight_bypassed = {}
|
|
hl.on("window.open", function(w)
|
|
if not (w.pid and w.pid > 0 and w.class) then return end
|
|
if bluelight_bypassed[w.class] then return end
|
|
if get_process_env(w.pid, "NO_BLUELIGHT") == "1" then
|
|
bluelight_bypassed[w.class] = true
|
|
hl.window_rule({ match = { class = "^" .. regex_escape(w.class) .. "$" }, content = "game" })
|
|
end
|
|
end)
|
|
|
|
-- System
|
|
hl.window_rule({ match = { class = "com.saivert.pwvucontrol" }, float = true })
|
|
|
|
hl.window_rule({
|
|
match = { class = "scrrec" },
|
|
float = true, pin = true, idle_inhibit = "always",
|
|
rounding = 10, opacity = 0.6, border_size = 0,
|
|
size = { 300, 100 }, move = { "1%", "1%" }, monitor = 0,
|
|
no_initial_focus = true
|
|
})
|
|
|
|
hl.window_rule({ match = { class = "com.gabm.satty" }, float = true, min_size = { 700, 400 } })
|
|
|
|
-- Layer Rules
|
|
hl.layer_rule({ match = { namespace = "quickshell:.*" }, blur = true, ignore_alpha = 0.79 })
|
|
hl.layer_rule({ match = { namespace = "quickshell:popout" }, animation = "bouncy" })
|