Files
dotfiles/dot_config/hypr/hyprland.d.lua/rules.lua.tmpl
T
mpuchstein 7b1ff73004 chezmoi: split tags into compositor/apps, data-drive workspace placement
Collapse redundant template tags into a cleaner data model:
- drop the desktop tag (use "not laptop"); replace hyprland/niri with
  compositor = "hyprland"|"niri"; replace cs2/entertainment with an
  apps list (gated via has); drop the dead waybar tag.
- move app->workspace->monitor routing into a portable [[data.placement]]
  table keyed by monitor role (left/right/primary), resolved per machine
  with fallback to the primary monitor. workspaces.lua.tmpl and
  rules.lua.tmpl now generate the workspace/window rules from it, so
  single-monitor machines work with no hardcoded monitor names.

Update CLAUDE.md / AGENTS.md / GEMINI.md schema references to match.
2026-06-03 22:53:36 +02:00

116 lines
4.2 KiB
Cheetah

{{- $apps := .chezmoi.config.data.apps -}}
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 })
-- App placement (data-driven from .placement; one workspace per entry, monitor
-- binding lives on the matching workspace_rule in workspaces.lua).
{{- range $p := .placement }}
{{- if or (not (hasKey $p "requires")) (has $p.requires $apps) }}
{{- $classes := list }}{{ range $c := $p.apps }}{{ $classes = append $classes ($c | replace "." "\\\\.") }}{{ end }}
hl.window_rule({ match = { class = "^({{ join "|" $classes }})$" }, workspace = "{{ $p.id }}{{ if $p.silent }} silent{{ end }}" })
{{- end }}
{{- 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" })