dot

vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks

343 lines10715 bytesLua
1-- Generated by hyprlang2lua. Review TODOs before reloading Hyprland.
2
3local mod = "SUPER"
4
5--GENERAL#
6
7--Monitor settings
8hl.monitor({
9 output = "HDMI-A-2",
10 mode = "2560x1440@280",
11 position = "0x0",
12 scale = 1,
13 vrr = 0,
14})
15
16hl.monitor({
17 output = "DP-1",
18 mode = "2560x1440@144",
19 position = "2560x-900",
20 scale = 1,
21 transform = 1,
22 vrr = 0,
23})
24
25--monitor=DP-1,disable
26
27-- WS on HDMI-A-2
28hl.workspace_rule({
29 workspace = "1",
30 monitor = "HDMI-A-2",
31 persistent = true,
32})
33
34hl.workspace_rule({
35 workspace = "2",
36 monitor = "HDMI-A-2",
37 persistent = true,
38})
39
40-- WS on DP-1
41hl.workspace_rule({
42 workspace = "3",
43 monitor = "DP-1",
44 persistent = true,
45})
46
47hl.workspace_rule({
48 workspace = "4",
49 monitor = "DP-1",
50 persistent = true,
51})
52
53hl.workspace_rule({
54 workspace = "5",
55 monitor = "DP-1",
56 persistent = true,
57})
58
59hl.window_rule({
60 name = "windowrule-1",
61 match = {
62 title = "^RuneLite.*",
63 },
64 float = true,
65})
66
67hl.window_rule({
68 name = "windowrule-2",
69 match = {
70 class = "net-runelite-client-RuneLite",
71 title = "win.*",
72 },
73 no_focus = true,
74})
75
76hl.window_rule({
77 name = "windowrule-3",
78 match = {
79 class = ".*",
80 },
81 immediate = true,
82})
83
84-- Battle.net (Proton via Faugus). The class is shared by every umu game without
85-- its own ID, so match on the title too.
86hl.window_rule({
87 name = "battlenet-float",
88 match = {
89 class = "^steam_app_default$",
90 title = "^Battle\\.net.*",
91 },
92 float = true,
93})
94
95-- xembedsniproxy (pkgs/xembedsniproxy) puts Wine/Proton tray icons such as
96-- Battle.net into Waybar's tray. It keeps a small helper window mapped for the
97-- icons it hosts, which Hyprland would otherwise show as a floating window.
98hl.window_rule({
99 name = "xembedsniproxy",
100 match = {
101 class = "^xembedsniproxy$",
102 },
103 opacity = "0.0 override 0.0 override",
104 border_size = 0,
105 no_blur = true,
106 no_shadow = true,
107 no_anim = true,
108 no_focus = true,
109 no_initial_focus = true,
110})
111
112-- Dolphin transparency now comes from Kvantum's own translucent_windows
113-- (see kvantum/RiceDark.kvconfig) rather than a forced window opacity here:
114-- Kvantum punches real per-pixel alpha into the background only, so text and
115-- icons stay fully opaque and legible, same mechanism as kitty (which also
116-- has no windowrule here) -- decoration.blur is already global+enabled above
117-- with ignore_opacity defaulting true, so it blurs through automatically.
118-- If Dolphin still looks flat/opaque after applying the Kvantum theme, that
119-- rule can come back as a fallback:
120-- hl.window_rule({
121-- name = "windowrule-4",
122-- match = { class = "^(org.kde.dolphin)$" },
123-- opacity = "0.90 0.80",
124-- })
125
126-- Claude's desktop app creates its window opaque (transparent: false in the
127-- Electron bundle) and has no user stylesheet, so unlike kitty and Dolphin it
128-- can't put alpha in the background only. A forced window opacity is the
129-- closest it gets: decoration.blur frosts what shows through, at the cost of
130-- the text being translucent too.
131hl.window_rule({
132 name = "claude-frosted",
133 match = { class = "^com\\.anthropic\\.Claude$" },
134 opacity = "0.80 0.80",
135})
136
137--windowrulev2 = maxsize 1803 1153,title:^(RuneLite)$
138--windowrulev2 = maxsize 1803 1153,title:^(RuneLite)$
139
140hl.layer_rule({
141 name = "layerrule-1",
142 match = {
143 namespace = "waybar",
144 },
145 blur = true,
146 ignore_alpha = 0,
147 blur_popups = true,
148})
149
150hl.layer_rule({
151 name = "layerrule-2",
152 match = {
153 namespace = "launcher", -- fuzzel
154 },
155 ignore_alpha = 0,
156 blur = true,
157})
158
159hl.layer_rule({
160 name = "layerrule-3",
161 match = {
162 namespace = "notifications",
163 },
164 ignore_alpha = 0,
165 blur = true,
166})
167
168hl.layer_rule({
169 name = "layerrule-4",
170 match = {
171 namespace = "eww-control-center",
172 },
173 ignore_alpha = 0,
174 blur = true,
175})
176
177hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("imsh-shot area -Cufa@~/.config/keys/scrnis -s1"))
178hl.bind(mod .. " + SHIFT + F", hl.dsp.exec_cmd("imsh-shot screen -Cua@~/.config/keys/scrnis"))
179hl.bind(mod .. " + SHIFT + R", hl.dsp.exec_cmd("imsh-cast -Cua@~/.config/keys/scrnis"))
180hl.bind(mod .. " + Return", hl.dsp.exec_cmd("~/.config/fuzzel/launch"))
181hl.bind(mod .. " + T", hl.dsp.exec_cmd("kitty"))
182hl.bind(mod .. " + W", hl.dsp.exec_cmd("firefox"))
183hl.bind(mod .. " + Q", hl.dsp.window.close())
184hl.bind(mod .. " + F", hl.dsp.window.float({ action = "toggle" }))
185hl.bind(mod .. " + G", hl.dsp.window.fullscreen({ mode = "fullscreen", action = "toggle" }))
186hl.bind(mod .. " + mouse_down", hl.dsp.focus({ workspace = "m+1" }))
187hl.bind(mod .. " + mouse_up", hl.dsp.focus({ workspace = "m-1" }))
188hl.bind(mod .. " + mouse:272", hl.dsp.window.drag())
189hl.bind(mod .. " + mouse:273", hl.dsp.window.resize())
190hl.bind(mod .. " + Delete", hl.dsp.exit())
191
192hl.env("LIBVA_DRIVER_NAME", "nvidia")
193hl.env("NVD_BACKEND", "direct") -- libva-nvidia-driver backend (Firefox hardware video decoding)
194hl.env("MOZ_DISABLE_RDD_SANDBOX", "1") -- let Firefox's video decoder process reach the NVIDIA driver
195hl.env("XDG_SESSION_TYPE", "wayland")
196hl.env("GBM_BACKEND", "nvidia-drm")
197hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
198hl.env("XCURSOR_THEME", "BreezeX-Black")
199hl.env("XCURSOR_SIZE", "28")
200hl.env("QT_QPA_PLATFORMTHEME", "qt6ct")
201
202hl.config({
203 general = {
204 border_size = 1,
205 gaps_in = 5,
206 gaps_out = 17,
207 layout = "dwindle",
208 col = {
209 active_border = "rgba(00000000)",
210 inactive_border = "rgba(00000000)",
211 },
212 allow_tearing = true,
213 },
214 decoration = {
215 rounding = 10,
216 -- No visible borders; the focused window is the one that isn't dimmed.
217 dim_inactive = true,
218 dim_strength = 0.15,
219 blur = {
220 enabled = true,
221 size = 9,
222 passes = 3,
223 new_optimizations = true,
224 popups = true, -- blur tooltips/context menus (Kvantum's popup_blurring is on for this)
225 },
226 shadow = {
227 enabled = false,
228 render_power = 3,
229 range = 4,
230 color = "rgba(1a1a1aee)",
231 },
232 },
233 input = {
234 kb_layout = "no",
235 follow_mouse = 1,
236 sensitivity = 0,
237 accel_profile = "flat",
238 },
239 --MISC#
240 misc = {
241 disable_hyprland_logo = true,
242 disable_splash_rendering = true,
243 -- Wallpaper's average colour instead of black, so the moment between the
244 -- login screen and hyprpaper drawing the wallpaper doesn't flash black.
245 background_color = "rgb(121d1a)",
246 },
247 ecosystem = {
248 no_donation_nag = true,
249 },
250 --BINDS#
251 binds = {
252 scroll_event_delay = 10,
253 },
254 --EXEC#
255 --ENVIROMENT#
256 --CURSOR#
257 cursor = {
258 -- Software cursor, for tearing. Hyprland (0.56) refuses to tear while a
259 -- hardware cursor is visible, because the kernel can't update the cursor
260 -- plane during a tearing flip. So with a hardware cursor a game only tears
261 -- while it hides the pointer (e.g. mouselook) and is vsynced otherwise, and
262 -- switching back and forth left WoW's cursor shape stale. With a software
263 -- cursor the game tears all the time; the price is that frames are
264 -- composited (one extra copy) while the pointer is visible.
265 no_hardware_cursors = true,
266 },
267 render = {
268 -- Fullscreen games (e.g. WoW) scan out straight to the monitor instead of
269 -- being copied by the compositor. With the software cursor above this only
270 -- applies while the game hides the pointer.
271 -- 1 = always; XWayland games don't set a content type, so 2 (auto) won't trigger.
272 direct_scanout = 1,
273 },
274})
275
276hl.on("hyprland.start", function()
277 -- Wallpaper, bar, notifications, the control centre, tray helpers, the
278 -- clipboard bridge and the polkit agent are systemd user services now
279 -- (modules/home/services.nix in the dot repo): uwsm starts them with
280 -- graphical-session.target, restarts them if they crash, and
281 -- `systemctl --user status <name>` / `journalctl --user -u <name>` show them.
282
283 -- Apps opened at login, in their usual places (scripts/autostart-apps starts
284 -- them, from autostart-apps.service). These placement rules are switched off
285 -- again after two minutes, so opening the same apps by hand later behaves like
286 -- any other window.
287 local startup_rules = {
288 hl.window_rule({
289 name = "startup-signal",
290 match = { class = "^signal$" },
291 workspace = "3 silent",
292 }),
293 hl.window_rule({
294 name = "startup-discord",
295 match = { class = "^discord$" },
296 workspace = "3 silent",
297 }),
298 hl.window_rule({
299 name = "startup-faugus",
300 match = { class = "^io\\.github\\.Faugus\\.faugus-launcher$" },
301 workspace = "1 silent",
302 float = true,
303 size = { 430, 610 },
304 move = { 1901, 333 },
305 }),
306 }
307 hl.timer(function()
308 for _, rule in ipairs(startup_rules) do
309 rule:set_enabled(false)
310 end
311 end, { timeout = 120000, type = "oneshot" })
312
313 -- WowUp can't start hidden, so the window it opens at login is sent to a hidden
314 -- workspace and closed again, which leaves it in the tray. That rule must not
315 -- outlive the one window: it is dropped as soon as the window has opened (or
316 -- after a minute if none came), so opening WowUp yourself right after login
317 -- lands on your workspace like any other window.
318 local wowup_rule = hl.window_rule({
319 name = "startup-wowup",
320 match = { class = "^WowUpCf$" },
321 workspace = "special:autostart silent",
322 })
323 local wowup_pending = true
324 local function wowup_done()
325 wowup_pending = false
326 wowup_rule:set_enabled(false)
327 end
328 hl.on("window.open", function(window)
329 if wowup_pending and window.class == "WowUpCf" then
330 wowup_done()
331 -- closing it from inside the open event is too early and gets ignored
332 local address = window.address
333 hl.timer(function()
334 hl.dispatch(hl.dsp.window.close({ window = "address:" .. address }))
335 end, { timeout = 300, type = "oneshot" })
336 end
337 end)
338 hl.timer(wowup_done, { timeout = 60000, type = "oneshot" })
339
340 -- hl.exec_cmd("gsettings set org.gnome.desktop.interface color-scheme prefer-dark")
341 -- hl.exec_cmd("gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark")
342end)
343