diff options
Diffstat (limited to 'working/hyprland-lua-port/hyprland.lua.generated')
| -rw-r--r-- | working/hyprland-lua-port/hyprland.lua.generated | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/working/hyprland-lua-port/hyprland.lua.generated b/working/hyprland-lua-port/hyprland.lua.generated new file mode 100644 index 0000000..04b30b9 --- /dev/null +++ b/working/hyprland-lua-port/hyprland.lua.generated @@ -0,0 +1,674 @@ +-- Hyprland Configuration +-- Translated from DWM config.def.h and sxhkdrc +-- Craig Jennings <c@cjennings.net> + +-- ============================================================================ +-- Monitor Configuration +-- ============================================================================ +-- Generated by hyprlang2lua. Review TODOs before reloading Hyprland. + +-- hyprlang2lua polyfills — runtime helpers reproducing +-- hyprlang behaviour the typed Lua API doesn't expose directly. + +local function hl_source_glob(pattern) + -- 'source = path/*.conf' had hyprlang glob and inline-expand the + -- matches. require() can't glob, so we shell out to ls (matching + -- the user's brace-expansion behaviour) and dofile each result. + -- Paths with spaces or shell metacharacters in the directory + -- portion will misparse; typical ~/.config/hypr/ layouts don't + -- hit this. Swap to lfs.dir() or find -name if you need fancier. + local p = io.popen("ls " .. pattern .. " 2>/dev/null") + if not p then return end + for f in p:lines() do + local chunk, err = loadfile(f) + if chunk then chunk() + else io.stderr:write("hl_source_glob: " .. tostring(err) .. "\n") end + end + p:close() +end + +hl.monitor({ + output = "", + mode = "preferred", + position = "auto", + scale = "auto", +}) + +-- Waybar's strip (6px top margin + 54px bar) is reserved statically by +-- waybar-reserve, and waybar runs with "exclusive": false. The bar's own +-- exclusive zone would vanish and reappear on every SIGUSR2 reload (the +-- collapse mechanism) and on hide/crash/relaunch, snapping every tiled window +-- up and back down. The static reservation holds the clients in place; only +-- the bar itself changes. `exec` (not exec-once) reruns it on every config +-- reload, which is exactly when Hyprland resets dynamic reservations. The +-- script is idempotent, and a catch-all `monitor=,addreserved,...` rule can't +-- replace it (empty-name addreserved silently no-ops). +-- +-- Run three times over ~0.6s, not once: on reload Hyprland clears the +-- reservation AND re-fires this exec, and the two race. A single run that +-- fires before the clear no-ops (reserved still looks correct), the clear then +-- wins, and the non-exclusive bar drops off-screen. Re-applying past the clear +-- window makes the restore reliable; the script is idempotent so extra runs are +-- free. Applying a monitor rule (e.g. the DP-4 pin) also clears the reservation, +-- so this covers a reload that re-asserts monitors too. + +-- ============================================================================ +-- Startup Applications +-- ============================================================================ +-- Portal and D-Bus setup FIRST, then waybar (needs portal for appearance query) +-- Start hyprland-session.target FIRST: it pulls up graphical-session.target, +-- which xdg-desktop-portal 1.22+ hard-requires (Requisite=). A bare-exec Hyprland +-- session has no session manager to raise that target, so without this the portal +-- fails its dependency at every login (screen-share + file pickers dead). +-- 'systemctl start' blocks until active, so the ';' sequence guarantees the target +-- is up before the portal restart runs. +-- +-- Portal restart (not start) reconnects stale portals on Hyprland restart. +-- Backend portals (GTK, Hyprland) restart BEFORE the main portal to avoid a 50s +-- GTK settings proxy timeout; the sequence keeps that ordering. Separated by ';' +-- not '&&' so a failing portal restart can't stop waybar from launching — waybar +-- degrades gracefully without the portal (only the appearance query is missed), +-- and gating the bar behind the portal left the desktop bar-less whenever +-- xdg-desktop-portal failed its dependency at login. Waybar stays gated on its +-- own config generation (waybar-active-config && waybar). + +-- Core services + +-- Desktop appearance +-- `settings restore` replays the remembered toggles and reapplies the stored +-- wallpaper. It replaced `waypaper --restore` on 2026-08-14: waypaper keeps +-- its own config.ini and the settings store keeps another, neither knew about +-- the other, and the login replay always won — so a wallpaper chosen in the +-- panel came back as whatever the shell had last set. The store is the only +-- one of the two that can hold a sun pair, a video or a projected face, so it +-- owns the restore. set-wallpaper records into it for choices made outside +-- the panel. +-- +-- waypaper --restore stays as the fallback, not the owner. If the stored +-- wallpaper cannot be applied (an image deleted, a drive not mounted yet), +-- `settings restore` exits 3 and waypaper's independent copy still puts +-- something on the screen. Dropping it outright would trade this bug for a +-- bare desktop. +-- +-- The wallpaper half only. The toggle half runs from its own exec-once further +-- down, after hypridle and dunst — caffeine *is* "hypridle isn't running" and +-- DND *is* dunst's pause level, so replaying them here would spend the whole +-- re-assert budget correcting backings that have not launched yet, and would +-- replay them a second time besides. This slot exists for awww's timing, not +-- theirs. + +-- Background services +-- hypridle is reaped on both exit paths, because it outlives its compositor +-- otherwise. An orphaned daemon keeps firing idle actions at whatever session +-- is live next, and it holds its old logind session scope open (the scope can't +-- close while a process sits in it), so orphans accumulate one per abnormal +-- session death. On 2026-07-22 velox reached five concurrent hypridle daemons; +-- two of them racing to lock produced "Cannot re-lock" and a session wedged +-- locked with no client able to draw a password prompt — recoverable only from +-- another console. exec-shutdown covers a clean compositor exit; the pkill in +-- exec-once covers the paths where it never runs (crash, SIGKILL, TTY logout). +hl.on("hyprland.shutdown", function() + hl.exec_cmd("pkill -x hypridle") +end) + +-- hypridle.conf is rendered here rather than tracked, because its contents +-- are this machine's stage times and hibernate setting. Tracking the render +-- meant every panel change dirtied the repo, and whichever machine +-- committed last imposed its policy on the others: a desktop ended up +-- carrying a laptop's suspend-then-hibernate line that it cannot run. +-- Rendering at session start makes the store the only source of truth and +-- the file a build artifact. +-- +-- hypridle-start owns the render, the fallback, and the ordering between +-- them, because that ordering is subtle enough to get wrong in a config +-- line nothing can test: a render can fail on purpose (a damaged store, to +-- avoid overwriting a real policy with defaults), and a fallback that +-- fired there would perform exactly the overwrite the render refused. +-- Replay the toggles that have no durable state of their own. Caffeine *is* +-- "hypridle isn't running" and DND *is* dunst's pause level, so the two +-- exec-once lines above (and dunst's) recreate both at a fixed default every +-- start — a deliberately-set caffeine was silently discarded on every login. +-- Ordered after those launches so it corrects a backing that exists; it also +-- re-asserts for a few seconds, which covers a backing that comes up late. +-- Logged like its neighbours: a silent exec-once failure here would look +-- exactly like the bug it fixes, and gammastep is the standing proof that a +-- launch dying quietly at session start can go unnoticed for a long time. + +-- Pyprland (scratchpads, magnify, etc.) + +-- Tray apps. wait-for-tray blocks until waybar's systray host is up (a fixed +-- sleep can't cover a slow cold-start waybar), so these register their icons +-- instead of opening as windows. Caps at ~30s, then launches anyway. +-- QT_FONT_DPI bumps the bridge's QML UI font (qt6ct General font is ignored by Qt Quick) + +-- ============================================================================ +-- Environment Variables +-- ============================================================================ +hl.env("XCURSOR_SIZE", "24") +hl.env("XCURSOR_THEME", "Bibata-Modern-Ice") +hl.env("XDG_CURRENT_DESKTOP", "Hyprland") +hl.env("XDG_SESSION_TYPE", "wayland") +hl.env("XDG_SESSION_DESKTOP", "Hyprland") +hl.env("_JAVA_AWT_WM_NONREPARENTING", "1") + +-- ============================================================================ +-- Appearance (matching DWM colors) +-- ============================================================================ +-- DWM colors: gray1=#222222, gray2=#444444, gray3=#bbbbbb, gray4=#eeeeee, cyan=#daa520 + +hl.curve("myBezier", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } }) +hl.animation({ + leaf = "windows", + enabled = true, + speed = 2, + bezier = "myBezier", +}) +hl.animation({ + leaf = "windowsOut", + enabled = true, + speed = 2, + bezier = "default", + style = "popin 80%", +}) +hl.animation({ + leaf = "fade", + enabled = true, + speed = 2, + bezier = "default", +}) +hl.animation({ + leaf = "workspaces", + enabled = true, + speed = 2, + bezier = "default", +}) +hl.animation({ + leaf = "specialWorkspace", + enabled = true, + speed = 2, + bezier = "default", + style = "slidevert", +}) + +hl.window_rule({ + match = { + class = "^(xdg-desktop-portal-gtk)$", + }, + float = true, +}) + +hl.window_rule({ + match = { + class = "^(Gimp)$", + }, + float = true, +}) + +hl.window_rule({ + match = { + class = "^(caffeine)$", + }, + float = true, +}) + +hl.window_rule({ + match = { + class = "^(qalculate-gtk)$", + }, + float = true, +}) + +hl.window_rule({ + match = { + title = "^(Event Tester)$", + }, + float = true, +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.netpanel)$", + }, + float = true, + move = "100%-464 50", +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.btpanel)$", + }, + float = true, + move = "100%-424 50", +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.audiopanel)$", + }, + float = true, + move = "100%-444 50", +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.timerpanel)$", + }, + float = true, + move = "100%-444 50", +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.settingspanel)$", + }, + float = true, + move = "100%-584 50", +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.weatherpanel)$", + }, + float = true, + move = "100%-464 50", +}) + +hl.window_rule({ + match = { + class = "^(net\\.cjennings\\.maintpanel)$", + }, + float = true, + move = "100%-1004 50", +}) + +hl.window_rule({ + match = { + title = "^(org-capture)$", + }, + float = true, + center = true, +}) + +hl.window_rule({ + match = { + title = "^(dirvish)$", + }, + float = true, + size = "1100 700", + center = true, +}) + +hl.window_rule({ + match = { + class = "^(Civ5XP)$", + }, + fullscreen = true, +}) + +local mod = "SUPER" + +hl.bind(mod .. " + T", hl.dsp.exec_cmd("foot")) +hl.bind(mod .. " + E", hl.dsp.exec_cmd("emacsclient -c -a \"\" || emacs")) +hl.bind(mod .. " + SHIFT + E", hl.dsp.exec_cmd("emacs")) +hl.bind(mod .. " + N", hl.dsp.exec_cmd("quick-capture")) +hl.bind(mod .. " + W", hl.dsp.exec_cmd("$BROWSER")) +hl.bind(mod .. " + F", hl.dsp.exec_cmd("dirvish-popup")) +hl.bind(mod .. " + SHIFT + F", hl.dsp.exec_cmd("layout-cycle float-toggle")) + +hl.bind(mod .. " + SPACE", hl.dsp.exec_cmd("fuzzel-toggle")) +hl.bind(mod .. " + SHIFT + W", hl.dsp.exec_cmd("$ALTBROWSER")) +hl.bind(mod .. " + P", hl.dsp.exec_cmd("media-toggle-all")) +hl.bind(mod .. " + SHIFT + L", hl.dsp.exec_cmd("calibre")) +hl.bind(mod .. " + SHIFT + P", hl.dsp.exec_cmd("toggle-touchpad")) + +hl.bind(mod .. " + J", hl.dsp.exec_cmd("layout-navigate next")) +hl.bind(mod .. " + K", hl.dsp.exec_cmd("layout-navigate prev")) +hl.bind(mod .. " + SHIFT + J", hl.dsp.exec_cmd("layout-navigate next move")) +hl.bind(mod .. " + SHIFT + K", hl.dsp.exec_cmd("layout-navigate prev move")) +hl.bind(mod .. " + H", hl.dsp.exec_cmd("layout-resize shrink")) +hl.bind(mod .. " + L", hl.dsp.exec_cmd("layout-resize grow")) +hl.bind(mod .. " + RETURN", hl.dsp.layout("swapwithmaster && sleep 0.05 && hyprctl dispatch layoutmsg focusmaster master")) +hl.bind(mod .. " + G", hl.dsp.window.center()) +hl.bind(mod .. " + TAB", hl.dsp.focus({ workspace = "previous" })) +hl.bind(mod .. " + SHIFT + C", hl.dsp.window.close()) + +hl.bind(mod .. " + SHIFT + RIGHT", hl.dsp.exec_cmd("layout-cycle next")) +hl.bind(mod .. " + SHIFT + LEFT", hl.dsp.exec_cmd("layout-cycle prev")) +hl.bind(mod .. " + SHIFT + T", hl.dsp.exec_cmd("hyprctl keyword general:layout master && hyprctl keyword master:orientation left")) +hl.bind(mod .. " + SHIFT + M", hl.dsp.exec_cmd("hyprctl keyword general:layout monocle")) +hl.bind(mod .. " + SHIFT + SPACE", hl.dsp.window.float({ action = "toggle" })) + +hl.bind(mod .. " + U", hl.dsp.layout("addmaster")) +hl.bind(mod .. " + D", hl.dsp.layout("removemaster")) + +hl.bind(mod .. " + O", hl.dsp.exec_cmd("stash-window")) +hl.bind(mod .. " + ALT + O", hl.dsp.exec_cmd("stash-others")) +hl.bind(mod .. " + SHIFT + O", hl.dsp.exec_cmd("stash-restore")) + +hl.bind(mod .. " + MINUS", hl.dsp.exec_cmd("window-gaps narrow")) +hl.bind(mod .. " + EQUAL", hl.dsp.exec_cmd("window-gaps widen")) +hl.bind(mod .. " + SHIFT + EQUAL", hl.dsp.exec_cmd("window-gaps reset")) +hl.bind(mod .. " + SHIFT + MINUS", hl.dsp.exec_cmd("window-gaps zero")) + +hl.bind(mod .. " + SHIFT + D", hl.dsp.exec_cmd("dim-toggle")) +hl.bind(mod .. " + SHIFT + G", hl.dsp.exec_cmd("settings-panel")) + +hl.bind(mod .. " + I", hl.dsp.exec_cmd("caffeine-toggle")) + +hl.bind(mod .. " + SHIFT + X", hl.dsp.exec_cmd("airplane-mode")) + +hl.bind(mod .. " + B", hl.dsp.exec_cmd("waybar-toggle")) + +hl.bind(mod .. " + bracketleft", hl.dsp.exec_cmd("waybar-collapse left")) +hl.bind(mod .. " + bracketright", hl.dsp.exec_cmd("waybar-collapse right")) + +hl.bind(mod .. " + F11", hl.dsp.window.fullscreen({ mode = "fullscreen", action = "toggle" })) + +hl.bind(mod .. " + 1", hl.dsp.focus({ workspace = 1 })) +hl.bind(mod .. " + 2", hl.dsp.focus({ workspace = 2 })) +hl.bind(mod .. " + 3", hl.dsp.focus({ workspace = 3 })) +hl.bind(mod .. " + 4", hl.dsp.focus({ workspace = 4 })) +hl.bind(mod .. " + 5", hl.dsp.focus({ workspace = 5 })) +hl.bind(mod .. " + 6", hl.dsp.focus({ workspace = 6 })) +hl.bind(mod .. " + 7", hl.dsp.focus({ workspace = 7 })) +hl.bind(mod .. " + 8", hl.dsp.focus({ workspace = 8 })) +hl.bind(mod .. " + 9", hl.dsp.focus({ workspace = 9 })) + +hl.bind(mod .. " + SHIFT + 1", hl.dsp.window.move({ workspace = 1, follow = false })) +hl.bind(mod .. " + SHIFT + 2", hl.dsp.window.move({ workspace = 2, follow = false })) +hl.bind(mod .. " + SHIFT + 3", hl.dsp.window.move({ workspace = 3, follow = false })) +hl.bind(mod .. " + SHIFT + 4", hl.dsp.window.move({ workspace = 4, follow = false })) +hl.bind(mod .. " + SHIFT + 5", hl.dsp.window.move({ workspace = 5, follow = false })) +hl.bind(mod .. " + SHIFT + 6", hl.dsp.window.move({ workspace = 6, follow = false })) +hl.bind(mod .. " + SHIFT + 7", hl.dsp.window.move({ workspace = 7, follow = false })) +hl.bind(mod .. " + SHIFT + 8", hl.dsp.window.move({ workspace = 8, follow = false })) +hl.bind(mod .. " + SHIFT + 9", hl.dsp.window.move({ workspace = 9, follow = false })) + +hl.bind(mod .. " + COMMA", hl.dsp.focus({ monitor = -1 })) +hl.bind(mod .. " + PERIOD", hl.dsp.focus({ monitor = "+1" })) +hl.bind(mod .. " + SHIFT + COMMA", hl.dsp.window.move({ monitor = "-1" })) +hl.bind(mod .. " + SHIFT + PERIOD", hl.dsp.window.move({ monitor = "+1" })) + +hl.bind(mod .. " + SHIFT + RETURN", hl.dsp.exec_cmd("pypr toggle term")) +hl.bind(mod .. " + A", hl.dsp.exec_cmd("audio-panel")) +hl.bind(mod .. " + R", hl.dsp.exec_cmd("pypr toggle monitor")) +hl.bind(mod .. " + SHIFT + N", hl.dsp.exec_cmd("net panel")) +hl.bind(mod .. " + SLASH", hl.dsp.exec_cmd("pypr toggle music")) + +hl.bind(mod .. " + Z", hl.dsp.exec_cmd("pypr zoom")) +hl.bind(mod .. " + Z", hl.dsp.submap("zoom")) + +hl.define_submap("zoom", function() + hl.bind("ESCAPE", hl.dsp.exec_cmd("pypr zoom 1")) + hl.bind("ESCAPE", hl.dsp.submap("reset")) + hl.bind(mod .. " + Z", hl.dsp.exec_cmd("pypr zoom 1")) + hl.bind(mod .. " + Z", hl.dsp.submap("reset")) +end) + +hl.bind(mod .. " + X", hl.dsp.exec_cmd("calc-toggle")) +hl.bind(mod .. " + C", hl.dsp.exec_cmd("hyprpicker -a")) +hl.bind(mod .. " + CONTROL + C", hl.dsp.exec_cmd("clock-panel toggle")) + +hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("pactl set-sink-volume @DEFAULT_SINK@ +5%"), { locked = true, repeating = true }) +hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("pactl set-sink-volume @DEFAULT_SINK@ -5%"), { locked = true, repeating = true }) +hl.bind("XF86AudioMute", hl.dsp.exec_cmd("audio quick-mute"), { locked = true }) +hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s +10%"), { locked = true, repeating = true }) +hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 10%-"), { locked = true, repeating = true }) + +hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("mic-toggle"), { locked = true }) + +hl.bind(mod .. " + SHIFT + A", hl.dsp.exec_cmd("audio ptt-toggle")) + +hl.bind(mod .. " + SHIFT + B", hl.dsp.exec_cmd("bt-panel")) + +hl.bind(mod .. " + S", hl.dsp.exec_cmd("screenshot region")) +hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("screenshot fullscreen")) +hl.bind("CTRL" .. mod .. " + S", hl.dsp.exec_cmd("screenshot fullscreen")) + +hl.bind(mod .. " + ESCAPE", hl.dsp.exec_cmd("hyprlock")) + +hl.bind(mod .. " + M", hl.dsp.exec_cmd("audio-cycle")) + +hl.bind(mod .. " + SHIFT + Q", hl.dsp.exec_cmd("pgrep -x wlogout || wlogout-menu")) +hl.bind(mod .. " + SHIFT + BACKSPACE", hl.dsp.submap("exitconfirm")) + +hl.define_submap("exitconfirm", function() + hl.bind("BACKSPACE", hl.dsp.exit()) + hl.bind("ESCAPE", hl.dsp.submap("reset")) + hl.bind("catchall", hl.dsp.submap("reset")) +end) + +hl.bind(mod .. " + SHIFT + ESCAPE", hl.dsp.exec_cmd("hyprctl reload")) +hl.bind("CTRL + ALT" .. mod .. " + K", hl.dsp.exec_cmd("hyprctl kill")) + +hl.bind(mod .. " + mouse:272", hl.dsp.window.drag()) +hl.bind(mod .. " + mouse:273", hl.dsp.window.resize()) +hl.bind(mod .. " + SHIFT + mouse:272", hl.dsp.window.resize()) + +-- Source: $HOME/.config/hypr/conf.d/*.conf (glob; resolved at runtime). Each matched .conf must be converted to .lua. +hl_source_glob("$HOME/.config/hypr/conf.d/*.lua") +hl.config({ + general = { + gaps_in = 25, + gaps_out = 30, + border_size = 2, + col = { + active_border = "rgba(daa520ff)", + inactive_border = "rgba(444444ff)", + nogroup_border_active = "rgba(daa520ff)", + nogroup_border = "rgba(444444ff)", + }, + -- Pyprland 3.4+ applies `group deny` to scratchpads, which routes their + -- border through col.nogroup_border* instead of col.*_border. Without + -- these overrides Hyprland's defaults paint scratchpads bright magenta. + layout = "master", + resize_on_border = true, + }, + decoration = { + rounding = 10, + dim_inactive = true, + dim_strength = 0.4, + dim_special = 0.2, + blur = { + enabled = false, + }, + shadow = { + enabled = false, + }, + }, + animations = { + enabled = true, + }, + -- ============================================================================ + -- Layout (master-stack like DWM tile) + -- ============================================================================ + master = { + new_status = "master", + new_on_top = true, + mfact = 0.55, + }, + dwindle = { + preserve_split = true, + }, + -- ============================================================================ + -- Input + -- ============================================================================ + cursor = { + no_warps = true, + inactive_timeout = 2.0, + }, + input = { + kb_layout = "us", + kb_options = "ctrl:nocaps", + numlock_by_default = true, + follow_mouse = 0, + -- 0, not the default 1: with follow_mouse off we never want focus to follow + -- the cursor. At 1, focus still jumps to the window under the pointer when it + -- crosses a floating<->tiled boundary, so launching a floating scratchpad (or + -- the org-capture popup) re-enabled focus-follows-mouse onto tiled windows. + float_switch_override_focus = 0, + mouse_refocus = false, + natural_scroll = true, + touchpad = { + natural_scroll = false, + }, + }, + -- ============================================================================ + -- Misc + -- ============================================================================ + misc = { + force_default_wallpaper = 0, + disable_hyprland_logo = true, + -- false so apps can't pull focus via activation requests. New windows still + -- focus on open (separate path); this stops e.g. a browser yanking focus + -- back off a freshly opened emacs frame. + focus_on_activate = false, + -- Let a fresh lock client adopt a session whose previous one died. The + -- default (off) is the strict reading of ext-session-lock: a dead lock + -- client leaves the session locked forever and refuses every replacement + -- ("Cannot re-lock"), so the screen stays up with nothing able to draw a + -- password prompt and the only way back in is another console. That is a + -- hard lockout, and it cost a session on velox 2026-07-22. On means a + -- replacement hyprlock re-attaches and prompts normally. The screen stays + -- locked either way — this decides whether the lock is recoverable, never + -- whether it holds. + allow_session_lock_restore = true, + }, + -- ============================================================================ + -- Debug (temporary - disable when stable) + -- ============================================================================ + debug = { + disable_logs = false, + }, + -- ============================================================================ + -- XWayland + -- ============================================================================ + xwayland = { + force_zero_scaling = true, + }, + -- ============================================================================ + -- Window Rules (Hyprland 0.53+ syntax: match:CONDITION, RULE) + -- ============================================================================ + -- Floating windows (from DWM rules) + -- net / bluetooth instrument-console panels. Normal floating windows (formerly + -- gtk4-layer-shell overlays) so they drag to move and corner-drag to resize. + -- Opened top-right to match their old anchored spot: the panel is right-aligned + -- with a 44px gap, so x = 100% - (window width + 44). net is 420 wide, bt 380. + -- maintenance console: the wide board (960), same right-aligned convention. + -- org-capture popup frame (quick-capture script names the frame) + -- Size is per-host in <host>/conf.d/local.conf: native window rules ignore + -- percentages (only pyprland honors them), so the popup is sized in absolute + -- pixels matching that host's terminal scratchpad. No size rule here means a + -- host without an override falls back to the script's char-cell geometry. + -- dirvish popup frame (dirvish-popup script names the frame). No stay_focused — + -- it's a file manager that launches files into other apps, so focus must be free + -- to follow; q (cj/dirvish-popup-quit) closes the frame. + -- NOTE: center windowrules removed 2026-03-04 per pyprland maintainer suggestion + -- Testing whether pyprland handles scratchpad re-centering natively (issue #211) + -- Gaming + -- ============================================================================ + -- Key Bindings + -- ============================================================================ + -- Terminal and core apps (from DWM) + -- Standalone emacs: its own process, not a frame on the daemon, so killing it + -- takes no other frame with it. init.el guards server-start on server-running-p, + -- so while the daemon holds the socket this process leaves it alone. With no + -- daemon up it finds no server and becomes one -- the guard lives in init.el, and + -- a keybind can't override it, since --eval runs after init. + -- From sxhkdrc + -- Window management (from DWM) + -- Layout-aware navigation (works across master, scrolling) + -- Swap focused window with master, then force focus onto the master slot. + -- swapwithmaster's own `master` focus param doesn't stick when invoked from + -- the master, so focusmaster master pins focus afterward. The 50ms sleep is + -- load-bearing: swapwithmaster fires an async focus event ~1-2ms after it + -- returns; without the delay that event lands AFTER focusmaster and flips + -- focus back to the detail. The sleep lets the swap's focus settle so + -- focusmaster runs last and wins. Proven via instrumented capture (19/19). + -- Layouts: master -> monocle + -- Cycle with Shift+arrows, or jump directly with Shift+T/M + -- (scrolling layout disabled until frame-fit + wrap-around work lands) + -- Master layout adjustments + -- Stash windows (hide to special workspace) + -- O = stash focused / Alt+O = stash others / Shift+O = restore all + -- Gaps between windows only; window-gaps leaves the monitor-edge gap + -- (general:gaps_out) fixed, so widening/narrowing moves the space between + -- windows, not the screen-edge margin. + -- Auto-dim toggle (D = dim). Same action as clicking the waybar custom/dim icon. + -- Caffeine (keep-awake) toggle. Same action as clicking the waybar + -- custom/caffeine icon — flips the hypridle daemon so the screen will / won't + -- lock. Stays on $mod+I ($mod+C is taken by hyprpicker; no free caffeine key). + -- Airplane mode (low-power: wifi off + CPU/brightness/services). A deliberate + -- keybind, not a bar click — engaging it disconnects you, so it shouldn't be a + -- misclick away. The custom/net module shows the state; this toggles it. + -- On Super+Shift+X ("X" = everything off); Super+Shift+A toggles push-to-talk. + -- Toggle bar visibility, or relaunch waybar if it crashed (no exec-once respawn). + -- Collapse / expand the left or right side of the bar to its base set + -- (same action as clicking the side's arrowhead). [ = left, ] = right. + -- Fullscreen + -- Workspaces 1-9 (from DWM TAGKEYS) + -- Move window to workspace (from DWM tag) + -- Monitor focus (from DWM focusmon) + -- ============================================================================ + -- Scratchpads (via pyprland) + -- ============================================================================ + -- Configured in ~/.config/hypr/pyprland.toml + -- Uses normal workspaces (not special), so new windows won't be captured + -- Magnify (zoom) + -- mod+Z zooms and enters the "zoom" submap; inside it, Escape or mod+Z + -- unzooms and returns to the normal keymap. Exit forces `pypr zoom 1` + -- (factor 1) so submap state and zoom state can't desync. Note: while + -- zoomed, other Hyprland binds pause until you exit the submap. + -- Calculator (not a scratchpad, just launches app) + -- Media/hardware keys + -- Microphone mute toggle (waybar pulseaudio#mic indicator follows via PipeWire events). + -- On the hardware mic-mute key. Super+Shift+A used to duplicate this; it now + -- toggles push-to-talk mode instead (mic-toggle stays reachable on the hw key). + -- Push-to-talk toggle: enter PTT (mic muted, hold key armed) / exit (restore). + -- The hold key is configurable (audio config ptt_key = Control_R or mouse:NNN). + -- Bluetooth panel (blueman retired in favor of the bt panel) + -- Screenshots (grim + slurp + fuzzel menu) + -- Shift+S captures the whole desktop with no pointer interaction, so it + -- works on scratchpads and popups that region-select would dismiss + -- Lock screen + -- Audio mute cycle (M for Mute): one key walks volume/mic through all four + -- on/off combinations. The waybar pulseaudio modules follow via PipeWire. + -- Exit/session + -- mod+Shift+Backspace no longer exits outright — it enters the "exitconfirm" + -- submap, where a second Backspace confirms and anything else backs out. + -- The bare bind killed two live sessions on 2026-07-22: it sits one key from + -- the mod+Shift cluster (Q wlogout, C killactive, Return terminal, G settings) + -- and took the whole session down with no prompt. wlogout owns the normal + -- exit path; this stays as the keyboard escape hatch for when wlogout won't + -- come up, so it's gated rather than removed. + -- Mouse bindings (from DWM buttons) + -- ============================================================================ + -- Machine-local overrides + -- ============================================================================ + -- Sourced last so machine-specific settings (monitor scale, gaps, keybinds) + -- override the defaults above. See conf.d/local.conf. +}) + +hl.on("hyprland.start", function() + hl.exec_cmd("dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE") + hl.exec_cmd("systemctl --user start hyprland-session.target; systemctl --user restart xdg-desktop-portal-hyprland xdg-desktop-portal-gtk; systemctl --user restart xdg-desktop-portal; waybar-active-config && waybar -c \"$XDG_RUNTIME_DIR/waybar/config\" -s ~/.config/waybar/style.css 2>&1 | grep -v \"LIBDBUSMENU-GLIB-WARNING\" > ~/.local/var/log/waybar-$(date +%Y-%m-%d-%H%M%S).log") + hl.exec_cmd("/usr/lib/polkit-kde-authentication-agent-1") + hl.exec_cmd("/usr/bin/gnome-keyring-daemon --start --components=pkcs11,secrets,ssh") + hl.exec_cmd("dunst > ~/.local/var/log/dunst-$(date +%Y-%m-%d-%H%M%S).log 2>&1") + hl.exec_cmd("awww-daemon & sleep 1 && { settings restore-wallpaper || waypaper --restore; }") + hl.exec_cmd("touchpad-auto") + hl.exec_cmd("pkill -x hypridle; hypridle-start > ~/.local/var/log/hypridle-$(date +%Y-%m-%d-%H%M%S).log 2>&1") + hl.exec_cmd("/usr/lib/geoclue-2.0/demos/agent") + hl.exec_cmd("gammastep > ~/.local/var/log/gammastep-$(date +%Y-%m-%d-%H%M%S).log 2>&1") + hl.exec_cmd("mpd") + hl.exec_cmd("settings restore > ~/.local/var/log/settings-restore-$(date +%Y-%m-%d-%H%M%S).log 2>&1") + hl.exec_cmd("pypr > ~/.local/var/log/pypr-$(date +%Y-%m-%d-%H%M%S).log 2>&1") + hl.exec_cmd("hypr-refocus-scratchpad") + hl.exec_cmd("wait-for-tray && signal-desktop --start-in-tray --ozone-platform=wayland") + hl.exec_cmd("env QT_FONT_DPI=108 protonmail-bridge --no-window") +end) + +hl.on("config.reloaded", function() + hl.exec_cmd("for i in 1 2 3; do sleep 0.2; waybar-reserve; done") +end) + |
