Files
hyprland-rising/hypr/hyprland.lua
T
aibrashandClaude Sonnet 5.5 211b3e626c Drop the remaining sway references
lock.sh no longer falls back to swaylock (hyprlock instead), and the
comments in hyprland.lua and the README no longer mention sway.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 14:28:06 +05:00

277 lines
15 KiB
Lua

-- Hyprland 0.57 (Lua). Clean dark work setup; shell = DankMaterialShell.
-- Fallback login session: GNOME (via GDM).
local terminal = "kitty"
local mod = "SUPER"
----------------
---- OUTPUT ----
----------------
-- eDP-1 at 1.25 scale is 1536 logical px wide, so HDMI sits right of it.
hl.monitor({ output = "eDP-1", mode = "preferred", position = "0x0", scale = 1.25 })
hl.monitor({ output = "HDMI-A-1", mode = "preferred", position = "1536x0", scale = 1 })
hl.monitor({ output = "", mode = "preferred", position = "auto", scale = "auto" })
-----------------
---- STARTUP ----
-----------------
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
hl.env("XDG_CURRENT_DESKTOP", "Hyprland")
hl.env("XDG_SESSION_TYPE", "wayland")
hl.env("QT_QPA_PLATFORM", "wayland;xcb")
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "auto")
hl.on("hyprland.start", function()
hl.exec_cmd("dbus-update-activation-environment --systemd WAYLAND_DISPLAY DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_TYPE")
hl.exec_cmd("systemctl --user import-environment WAYLAND_DISPLAY DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_TYPE")
-- portals (file pickers, screen sharing, Nautilus) need graphical-session.target, which is not started outside uwsm
hl.exec_cmd("systemctl --user start hyprland-session.target")
hl.exec_cmd("sh -c 'pgrep -x dms >/dev/null || exec dms run'")
hl.exec_cmd("systemctl --user start hyprpolkitagent")
hl.exec_cmd("hypridle")
hl.exec_cmd("nm-applet --indicator")
hl.exec_cmd("wl-paste --type text --watch cliphist store")
hl.exec_cmd("wl-paste --type image --watch cliphist store")
hl.exec_cmd(terminal .. " --class scratch")
hl.exec_cmd("sh -c 'sleep 5; flatpak info com.nextcloud.desktopclient.nextcloud >/dev/null 2>&1 && ! pgrep -f \"[n]extcloud.*--background\" >/dev/null && flatpak run com.nextcloud.desktopclient.nextcloud --background'")
hl.exec_cmd("sh -c 'sleep 5; flatpak info org.remmina.Remmina >/dev/null 2>&1 && ! pgrep -f \"[R]emmina.* -i\" >/dev/null && flatpak run org.remmina.Remmina -i'")
hl.exec_cmd("sh -c 'sleep 8; command -v geary >/dev/null && ! pgrep -x geary >/dev/null && geary --gapplication-service'")
end)
-----------------------
---- LOOK AND FEEL ----
-----------------------
hl.config({
general = {
gaps_in = 6,
gaps_out = 14,
border_size = 2,
col = {
active_border = { colors = { "rgba(e91e63ff)", "rgba(ff6398ff)", "rgba(f8bbd9ff)", "rgba(e91e63ff)" }, angle = 45 },
inactive_border = "rgba(3a2a2eaa)",
},
resize_on_border = true,
allow_tearing = false,
layout = "dwindle",
},
decoration = {
rounding = 16,
rounding_power = 4, -- squircle corners
active_opacity = 1.0,
inactive_opacity = 0.96,
dim_inactive = true,
dim_strength = 0.06,
shadow = {
enabled = true,
range = 30,
render_power = 3,
color = 0x66e91e63, -- soft pink glow on the focused window
color_inactive = 0x66000000,
},
blur = {
enabled = true,
size = 10,
passes = 4,
noise = 0.02,
vibrancy = 0.3,
brightness = 1.05,
popups = true,
},
},
animations = { enabled = true },
binds = { workspace_back_and_forth = true },
dwindle = { preserve_split = true, force_split = 2 }, -- new window always opens to the right/below
xwayland = { force_zero_scaling = true }, -- crisp X11 apps on the 1.25x laptop panel
cursor = { hide_on_key_press = true, inactive_timeout = 5 },
misc = {
force_default_wallpaper = 0,
disable_hyprland_logo = true,
focus_on_activate = true,
key_press_enables_dpms = true, -- wake the screen on any key / mouse move (hypridle turns it off)
mouse_move_enables_dpms = true,
animate_manual_resizes = true,
animate_mouse_windowdragging = true,
},
})
hl.curve("easeOutQuint", { type = "bezier", points = { {0.23, 1}, {0.32, 1} } })
hl.curve("easeInOutCubic", { type = "bezier", points = { {0.65, 0.05}, {0.36, 1} } })
hl.curve("quick", { type = "bezier", points = { {0.15, 0}, {0.1, 1} } })
hl.curve("linear", { type = "bezier", points = { {0, 0}, {1, 1} } })
hl.curve("snappy", { type = "spring", mass = 1, stiffness = 340, damping = 24 })
hl.curve("bouncy", { type = "spring", mass = 1, stiffness = 280, damping = 17 })
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
hl.animation({ leaf = "border", enabled = true, speed = 4, bezier = "easeOutQuint" })
hl.animation({ leaf = "borderangle", enabled = true, speed = 60, bezier = "linear", style = "loop" }) -- slowly rotating gradient border
hl.animation({ leaf = "windows", enabled = true, speed = 3.2, spring = "snappy" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 3.0, spring = "bouncy", style = "popin 88%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.2, bezier = "quick", style = "popin 88%" })
hl.animation({ leaf = "fade", enabled = true, speed = 2, bezier = "quick" })
hl.animation({ leaf = "fadeDim", enabled = true, speed = 2.5, bezier = "quick" })
hl.animation({ leaf = "layers", enabled = true, speed = 2.6, bezier = "easeOutQuint" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 2.6, bezier = "easeOutQuint", style = "slide" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.0, bezier = "quick", style = "fade" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 3.0, bezier = "easeInOutCubic", style = "slide" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 2.8, bezier = "easeOutQuint", style = "slidevert" })
-- No gaps/border/rounding for a single tiled window.
hl.workspace_rule({ workspace = "w[tv1]", gaps_out = 0, gaps_in = 0 })
hl.workspace_rule({ workspace = "f[1]", gaps_out = 0, gaps_in = 0 })
hl.window_rule({ name = "smart-gaps-1", match = { float = false, workspace = "w[tv1]" }, border_size = 0, rounding = 0 })
hl.window_rule({ name = "smart-gaps-2", match = { float = false, workspace = "f[1]" }, border_size = 0, rounding = 0 })
---------------
---- INPUT ----
---------------
hl.config({
input = {
kb_layout = "us,ru",
kb_options = "grp:alt_shift_toggle,grp_led:scroll",
follow_mouse = 2, -- click to focus; the cursor can still cross other windows
sensitivity = -0.5,
touchpad = {
natural_scroll = true,
tap_to_click = true,
tap_button_map = "lrm",
disable_while_typing = true,
middle_button_emulation = true,
},
},
})
hl.device({ name = "type:touchpad", sensitivity = 0.3 })
hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
---------------------
---- KEYBINDINGS ----
---------------------
local function b(keys, dsp, opts) hl.bind(keys, dsp, opts) end
local function dms(args) return hl.dsp.exec_cmd("dms ipc call " .. args) end
b(mod .. " + Return", hl.dsp.exec_cmd(terminal))
b(mod .. " + SHIFT + Q", hl.dsp.window.close())
b(mod .. " + D", dms("spotlight toggle"))
b(mod .. " + SHIFT + E", dms("powermenu toggle"))
b(mod .. " + SHIFT + P", dms("powermenu toggle"))
b(mod .. " + SHIFT + C", hl.dsp.reload_config())
b(mod .. " + N", dms("control-center toggle"))
b(mod .. " + SHIFT + N", dms("notifications toggle"))
b(mod .. " + SHIFT + V", dms("clipboard toggle"))
b(mod .. " + SHIFT + X", hl.dsp.exec_cmd("~/.config/hypr/scripts/lock.sh"))
b(mod .. " + O", hl.dsp.exec_cmd("nautilus --new-window"))
-- focus / move (vim keys + arrows)
local dirs = { h = "left", j = "down", k = "up", l = "right", left = "left", down = "down", up = "up", right = "right" }
for key, d in pairs(dirs) do
b(mod .. " + " .. key, hl.dsp.focus({ direction = d }))
b(mod .. " + SHIFT + " .. key, hl.dsp.window.move({ direction = d }))
end
-- layout
b(mod .. " + F", hl.dsp.window.fullscreen())
b(mod .. " + SHIFT + SPACE", hl.dsp.window.float({ action = "toggle" }))
b(mod .. " + SHIFT + F", hl.dsp.window.float({ action = "toggle" }))
b(mod .. " + C", hl.dsp.window.center())
b(mod .. " + E", hl.dsp.layout("togglesplit"))
b(mod .. " + P", hl.dsp.window.pseudo())
b(mod .. " + W", hl.dsp.group.toggle())
b("ALT + TAB", hl.dsp.exec_cmd("~/.config/hypr/scripts/prev-window.sh")) -- previous window (press again to go back)
b(mod .. " + ALT + TAB", hl.dsp.window.cycle_next()) -- next window on this workspace
b(mod .. " + TAB", hl.dsp.focus({ workspace = "e+1" }))
b(mod .. " + SHIFT + TAB", hl.dsp.focus({ workspace = "e-1" }))
b(mod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
b(mod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
-- workspaces 1..10
for i = 1, 10 do
local key = i % 10
b(mod .. " + " .. key, hl.dsp.focus({ workspace = i }))
b(mod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- scratchpad
b(mod .. " + grave", hl.dsp.workspace.toggle_special("scratch"))
b(mod .. " + SHIFT + minus", hl.dsp.window.move({ workspace = "special:scratch" }))
b(mod .. " + minus", hl.dsp.workspace.toggle_special("scratch"))
-- mouse
b(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
b(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- resize the focused window without a mode: Mod+Ctrl + h/j/k/l or arrows (hold to repeat)
local rs = { h = {-40, 0}, l = {40, 0}, k = {0, -40}, j = {0, 40}, left = {-40, 0}, right = {40, 0}, up = {0, -40}, down = {0, 40} }
for key, d in pairs(rs) do
b(mod .. " + CTRL + " .. key, hl.dsp.window.resize({ x = d[1], y = d[2], relative = true }), { repeating = true })
end
-- sysadmin toolbox
b(mod .. " + S", hl.dsp.exec_cmd("kitty --class ssh-menu -o font_size=12 -o env=SSH_MENU=1 --title 'ssh menu' ~/.config/hypr/scripts/ssh-menu.sh")) -- SSH hub (fzf)
local raise = "~/.config/hypr/scripts/raise.sh "
b(mod .. " + T", hl.dsp.exec_cmd(raise .. "'^org.telegram.desktop$' flatpak run org.telegram.desktop")) -- focus if open, else launch
b(mod .. " + B", hl.dsp.exec_cmd(raise .. "'^google-chrome$' flatpak run com.google.Chrome")) -- again = next Chrome window
b(mod .. " + SHIFT + B", hl.dsp.exec_cmd("flatpak run com.google.Chrome --new-window")) -- always a new window
b(mod .. " + F1", hl.dsp.exec_cmd("~/.config/hypr/scripts/cheatsheet.sh")) -- hotkey reference
b(mod .. " + K", hl.dsp.exec_cmd(raise .. "'^org.keepassxc.KeePassXC$' keepassxc"))
b(mod .. " + SHIFT + T", hl.dsp.window.pin()) -- keep a floating window on top
b(mod .. " + bracketright", hl.dsp.group.next())
b(mod .. " + bracketleft", hl.dsp.group.prev())
-- two monitors: laptop <-> external (focus only)
b(mod .. " + comma", hl.dsp.focus({ monitor = "-1" }))
b(mod .. " + period", hl.dsp.focus({ monitor = "+1" }))
-- screenshots (grim + slurp; saved to ~/Pictures/Screenshots and copied)
local shot = "mkdir -p ~/Pictures/Screenshots; f=~/Pictures/Screenshots/$(date +%F_%H-%M-%S).png; "
b("Print", hl.dsp.exec_cmd(shot .. "grim -g \"$(slurp)\" - | tee $f | wl-copy"))
b(mod .. " + SHIFT + S", hl.dsp.exec_cmd(shot .. "grim -g \"$(slurp)\" - | tee $f | wl-copy"))
b("SHIFT + Print", hl.dsp.exec_cmd(shot .. "grim - | tee $f | wl-copy"))
b("CTRL + Print", hl.dsp.exec_cmd(shot .. "grim -g \"$(hyprctl activewindow -j | jq -r '\"\\(.at[0]),\\(.at[1]) \\(.size[0])x\\(.size[1])\"')\" - | tee $f | wl-copy"))
b(mod .. " + CTRL + C", hl.dsp.exec_cmd("hyprpicker -a")) -- color picker
b(mod .. " + SHIFT + O", hl.dsp.exec_cmd("~/.config/hypr/scripts/ocr.sh")) -- screen region -> text in clipboard
-- media / brightness / volume via DMS (shows OSD)
b("XF86AudioRaiseVolume", dms("audio increment 5"), { locked = true, repeating = true })
b("XF86AudioLowerVolume", dms("audio decrement 5"), { locked = true, repeating = true })
b("XF86AudioMute", dms("audio mute"), { locked = true })
b("XF86AudioMicMute", dms("audio micmute"), { locked = true })
b("XF86MonBrightnessUp", dms("brightness increment 5 \"\""), { locked = true, repeating = true })
b("XF86MonBrightnessDown", dms("brightness decrement 5 \"\""), { locked = true, repeating = true })
b("XF86AudioPlay", dms("mpris playPause"), { locked = true })
b("XF86AudioNext", dms("mpris next"), { locked = true })
b("XF86AudioPrev", dms("mpris previous"), { locked = true })
-----------------
---- RULES ------
-----------------
hl.window_rule({ name = "suppress-maximize", match = { class = ".*" }, suppress_event = "maximize" })
hl.window_rule({
name = "xwayland-drag-fix",
match = { class = "^$", title = "^$", xwayland = true, float = true, fullscreen = false, pin = false },
no_focus = true,
})
hl.window_rule({
name = "float-utils",
match = { class = "^(pavucontrol|nm-connection-editor|blueman-manager|org.pulseaudio.pavucontrol)$" },
float = true, size = "700 500", center = true,
})
hl.window_rule({ name = "float-mpv", match = { class = "^mpv$" }, float = true })
hl.window_rule({ name = "idle-inhibit-fs", match = { class = "^(firefox|mpv|google-chrome|org.telegram.desktop)$" }, idle_inhibit = "fullscreen" })
-- no auto-lock in the middle of an RDP/VNC session
hl.window_rule({ name = "idle-inhibit-rdp", match = { class = "^(org.remmina.Remmina|com.freerdp.FreeRDP|xfreerdp)$" }, idle_inhibit = "focus" })
-- file dialogs from the portal and Chrome picture-in-picture
hl.window_rule({ name = "float-portal-dialogs", match = { class = "^xdg-desktop-portal-gtk$" }, float = true, size = "900 600", center = true })
hl.window_rule({ name = "pip", match = { title = "^Picture in picture$" }, float = true, pin = true, size = "480 270", move = "monitor_w-500 monitor_h-290" })
hl.window_rule({
name = "scratch-term",
match = { class = "^scratch$" },
workspace = "special:scratch silent", float = true, size = "1000 560", center = true,
})
hl.window_rule({ name = "float-ssh-menu", match = { class = "^ssh-menu$" }, float = true, size = "1400 780", center = true })
hl.window_rule({ name = "float-keepass", match = { class = "^org.keepassxc.KeePassXC$" }, float = true, size = "1300 860", center = true })
-- Blur the DMS surfaces (bar, launcher, popups, notifications).
hl.layer_rule({ name = "dms-blur", match = { namespace = "^dms:.*" }, blur = true, blur_popups = true, ignore_alpha = 0.3 })
hl.layer_rule({ name = "noanim-slurp", match = { namespace = "^selection$" }, no_anim = true })