-- Shared Hyprland profile. yadm deploys machine.lua for the active class. -- -- Scope: preserve launch, media, directional focus/move, workspace, scratchpad, -- floating, fullscreen, and window-placement muscle memory. i3's split-tree, -- stacking/tabbed, and resize-mode behavior is intentionally deferred. local machine = dofile(os.getenv("HOME") .. "/.config/hypr/machine.lua") for _, monitor in ipairs(machine.monitors or {}) do hl.monitor(monitor) end local main_mod = "SUPER" local terminal = "alacritty" local input = { kb_layout = "us", kb_variant = "intl", numlock_by_default = true, } for key, value in pairs(machine.input or {}) do input[key] = value end hl.config({ input = input, general = { gaps_in = 4, -- Keep a thin black perimeter so the bottom border remains on-screen. gaps_out = 2, border_size = 2, col = { -- Muted Ember tones: visible focus without a high-brightness OLED edge. active_border = "rgba(c9782ed9)", inactive_border = "rgba(2a190fa6)", }, }, decoration = { rounding = 0, }, animations = { enabled = false, }, binds = { -- Selecting the current workspace again returns to the last workspace. workspace_back_and_forth = true, }, }) -- Dynamically hide the border when a workspace has exactly one tiled app. hl.workspace_rule({ workspace = "w[t1]", no_border = true, }) -- Application and session bindings. local noctalia_ipc = "noctalia msg " -- Tap and release Super on its own to open Noctalia's control-center home. hl.bind(main_mod .. " + SUPER_L", hl.dsp.exec_cmd(noctalia_ipc .. "panel-toggle control-center"), { release = true }) hl.bind(main_mod .. " + RETURN", hl.dsp.exec_cmd(terminal)) hl.bind(main_mod .. " + CTRL + RETURN", hl.dsp.exec_cmd(terminal .. " -e zellij")) hl.bind(main_mod .. " + SHIFT + RETURN", hl.dsp.exec_cmd("rofi -show combi")) hl.bind(main_mod .. " + SPACE", hl.dsp.exec_cmd(noctalia_ipc .. "panel-toggle launcher")) hl.bind(main_mod .. " + S", hl.dsp.exec_cmd(noctalia_ipc .. "panel-toggle control-center")) hl.bind(main_mod .. " + SHIFT + F", hl.dsp.exec_cmd(noctalia_ipc .. "bar-toggle main")) hl.bind(main_mod .. " + SHIFT + R", hl.dsp.exec_cmd("hyprctl reload")) hl.bind(main_mod .. " + CTRL + L", hl.dsp.exec_cmd("command -v hyprlock >/dev/null && hyprlock || loginctl lock-session")) hl.bind(main_mod .. " + CTRL + SHIFT + ALT + S", hl.dsp.exec_cmd("systemctl suspend")) hl.bind(main_mod .. " + CTRL + SHIFT + ALT + H", hl.dsp.exec_cmd("systemctl hibernate")) hl.bind(main_mod .. " + SHIFT + C", hl.dsp.window.close()) hl.bind(main_mod .. " + CTRL + SHIFT + F", hl.dsp.exec_cmd("env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 flameshot gui")) -- Audio, media, brightness, and calculator keys. hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true }) hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { locked = true }) hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true }) hl.bind(main_mod .. " + XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), { locked = true }) hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true }) hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true }) hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true }) hl.bind(main_mod .. " + XF86AudioRaiseVolume", hl.dsp.exec_cmd("playerctl next"), { locked = true }) hl.bind(main_mod .. " + XF86AudioLowerVolume", hl.dsp.exec_cmd("playerctl previous"), { locked = true }) hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd('light -A 5'), { locked = true }) hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd('light -U 5'), { locked = true }) hl.bind(main_mod .. " + XF86MonBrightnessDown", hl.dsp.exec_cmd('light -S 0.0'), { locked = true }) hl.bind(main_mod .. " + XF86MonBrightnessUp", hl.dsp.exec_cmd('light -S 100'), { locked = true }) hl.bind("XF86Calculator", hl.dsp.exec_cmd("qalculate-qt"), { locked = true }) -- Retain i3's hjkl and arrow-key directional workflow. for _, direction in ipairs({ "left", "down", "up", "right" }) do local key = ({ left = "H", down = "J", up = "K", right = "L" })[direction] hl.bind(main_mod .. " + " .. key, hl.dsp.focus({ direction = direction })) hl.bind(main_mod .. " + " .. direction, hl.dsp.focus({ direction = direction })) end for key, direction in pairs({ H = "l", J = "d", K = "u", L = "r", LEFT = "l", DOWN = "d", UP = "u", RIGHT = "r" }) do hl.bind(main_mod .. " + SHIFT + " .. key, hl.dsp.window.move({ direction = direction })) end -- Numbered workspaces match the i3 profile. Shift moves; Ctrl moves and follows. for workspace = 1, 9 do local key = tostring(workspace) hl.bind(main_mod .. " + " .. key, hl.dsp.focus({ workspace = workspace })) hl.bind(main_mod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = workspace })) hl.bind(main_mod .. " + CTRL + " .. key, hl.dsp.window.move({ workspace = workspace, follow = true })) end -- Match i3's back-and-forth workflow: Super+Tab switches to the last workspace; -- selecting the current numbered workspace also returns to it. hl.bind(main_mod .. " + TAB", hl.dsp.focus({ workspace = "previous" })) hl.bind(main_mod .. " + CTRL + RIGHT", hl.dsp.focus({ workspace = "+1" })) hl.bind(main_mod .. " + CTRL + LEFT", hl.dsp.focus({ workspace = "-1" })) -- Hyprland special workspaces are the Wayland analogue of the i3 scratchpad. hl.bind(main_mod .. " + MINUS", hl.dsp.workspace.toggle_special("scratchpad")) hl.bind(main_mod .. " + SHIFT + MINUS", hl.dsp.window.move({ workspace = "special:scratchpad" })) -- Keep the i3 floating and fullscreen shortcuts. Layout-specific binds are deferred. hl.bind(main_mod .. " + F", hl.dsp.window.fullscreen({ action = "toggle" })) hl.bind(main_mod .. " + SHIFT + SPACE", hl.dsp.window.float({ action = "toggle" })) hl.bind(main_mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) hl.bind(main_mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) -- i3 for_window/assign translations. Verify Wayland class/title values with `hyprctl clients`. local function float_rule(name, match, border_size) hl.window_rule({ name = name, match = match, float = true, border_size = border_size, }) end hl.window_rule({ name = "thunderbird-workspace", match = { class = "^Thunderbird$" }, workspace = "9" }) float_rule("alsamixer", { title = "^alsamixer$" }, 1) float_rule("qalculate", { title = "^Qalculate!$" }) float_rule("psensor", { class = "^Psensor$" }) float_rule("lxrandr", { title = "^lxrandr$" }) float_rule("blueman", { class = "^Blueman-manager$" }) float_rule("gparted", { class = "^GParted$" }) float_rule("pavucontrol", { class = "^pavucontrol$" }) float_rule("volume-control", { title = "^Volume Control$" }) float_rule("virtualbox", { class = ".*VirtualBox.*" }) hl.window_rule({ name = "virtualbox-manager-tiles", match = { class = ".*VirtualBox Manager.*" }, tile = true }) hl.window_rule({ name = "virtualbox-machine-tiles", match = { class = ".*VirtualBox Machine.*" }, tile = true }) -- Send music clients to the existing scratchpad without stealing startup focus. hl.window_rule({ name = "spotify-music", match = { class = "^Spotify$" }, workspace = "special:scratchpad silent", no_initial_focus = true }) hl.window_rule({ name = "deezer-music", match = { class = "^Deezer$" }, workspace = "special:scratchpad silent", no_initial_focus = true }) hl.on("hyprland.start", function() for _, command in ipairs(machine.startup or {}) do hl.exec_cmd(command) end end)