Files
hyprland-rising/hypr/hyprland.lua
T
aibrashandClaude Sonnet 5.5 d6d8aa00c8 Replace the resize submap with direct Mod+Ctrl+hjkl binds
Mod+R entered a mode where every other bind stopped working until Esc/Enter,
with no indicator. Resizing is now Mod+Ctrl+H/J/K/L (or arrows, repeating).

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

261 lines
14 KiB
Lua

-- Hyprland 0.57 (Lua). Clean dark work setup; shell = DankMaterialShell.
-- Sway config stays in ~/.config/sway as a fallback session.
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; sway had sway-session.target for this
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 (sway smart_gaps equivalent).
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, like sway's focus_follows_mouse no
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
-- 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 })