hypr-dotfiles/hypr/hyprland.lua
Jeena f9553329ef fix(hypr): unify special workspace, drop 'magic' name
The Lua conversion had introduced a named 'magic' special workspace on the
SUPER+S keybinding while KeePassXC and the terminal still targeted the default
'special' workspace, splitting them into two separate scratchpads and making
KeePassXC unreachable. Route everything through the single default special
workspace to match the original .conf behavior.
2026-08-27 19:43:36 +09:00

285 lines
10 KiB
Lua

-- Hyprland Lua config (converted from hyprland.conf)
-- Refer to the wiki for more information: https://wiki.hypr.land/Configuring/Start/
----------------
--## ENVIRONMENT ###
----------------
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct")
hl.env("HYPRSHOT_DIR", "Screenshots")
-----------------
--## MONITORS ###
-----------------
-- Safe default for any machine
hl.monitor({
output = "",
mode = "preferred",
position = "auto",
scale = 1,
})
-------------------
--## MY PROGRAMS ###
-------------------
-- Shared program variables (was programs.conf). Other modules require() this.
local P = require("programs")
--################
--## AUTOSTART ###
--################
-- Hyprland-start executables (from hyprland.conf)
hl.on("hyprland.start", function()
hl.exec_cmd("hypridle")
hl.exec_cmd("waybar")
hl.exec_cmd("swaybg -i ~/.config/hypr/wallpapers/Purple.jpg -m fill")
hl.exec_cmd("~/.config/hypr/scripts/dynamic-borders.sh")
hl.exec_cmd("~/.config/hypr/scripts/xdg-desktop-portal-hyprland.sh")
end)
require("autostart")
-------------------
--## PERMISSIONS ###
-------------------
-- Note: permission changes require a Hyprland restart.
-- ecosystem { enforce_permissions = 1 } (commented out in original)
--####################
--## LOOK AND FEEL ###
--####################
-- General
hl.config({
general = {
gaps_in = 5,
gaps_out = { 1, 0, 0, 0 },
border_size = 3,
col = {
active_border = { colors = { "rgba(D55D7Cee)", "rgba(480023ee)" }, angle = 45 },
inactive_border = "rgba(595959aa)",
},
resize_on_border = true,
allow_tearing = false,
layout = "dwindle",
},
})
-- Decoration
hl.config({
decoration = {
rounding = 10,
rounding_power = 2,
active_opacity = 1.0,
inactive_opacity = 0.8,
shadow = {
enabled = true,
range = 4,
render_power = 3,
color = "rgba(1a1a1aee)",
},
blur = {
enabled = true,
size = 3,
passes = 1,
vibrancy = 0.1696,
},
},
})
-- Animations
hl.config({
animations = {
enabled = true,
},
})
-- Bezier curves
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("linear", { type = "bezier", points = { {0, 0}, {1, 1} } })
hl.curve("almostLinear", { type = "bezier", points = { {0.5, 0.5}, {0.75, 1.0} } })
hl.curve("quick", { type = "bezier", points = { {0.15, 0}, {0.1, 1} } })
-- Animations (default curves)
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" })
hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" })
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "easeOutQuint", style = "slidefade" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "easeOutQuint", style = "slidefade" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "easeOutQuint", style = "slidefade" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 1.21, bezier = "quick", style = "slidefadevert" })
-- Dwindle
hl.config({
dwindle = {
preserve_split = true, -- You probably want this
force_split = 2,
},
})
-- Master
hl.config({
master = {
new_status = "master",
},
})
-- Misc
hl.config({
misc = {
force_default_wallpaper = 0, -- Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = true, -- If true disables the random hyprland logo / anime girl background.
enable_swallow = true,
font_family = "FontAwesome",
-- Fix black screen on wake: let the compositor itself re-enable DPMS on input,
-- as a fallback when hypridle's after_sleep dpms-on runs too early / gets ignored.
key_press_enables_dpms = true,
mouse_move_enables_dpms = true,
},
})
---------------
--## INPUT ###
---------------
hl.config({
input = {
kb_layout = "us,se,kr",
kb_variant = "dvorak,dvorak",
kb_options = "grp:win_space_toggle,grp:alt_r_toggle",
follow_mouse = 0,
float_switch_override_focus = 0,
sensitivity = 0.33, -- -1.0 - 1.0, 0 means no modification.
natural_scroll = true,
accel_profile = "adaptive",
touchpad = {
natural_scroll = true,
tap_to_click = true,
disable_while_typing = true,
},
},
})
-- Gestures
hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
hl.gesture({ fingers = 3, direction = "vertical", action = function()
hl.dispatch(hl.dsp.workspace.toggle_special())
end })
-- Example per-device config
hl.device({
name = "epic-mouse-v1",
sensitivity = -0.5,
})
-------------------
--## KEYBINDINGS ###
-------------------
-- Main focus/workspace binds (from hyprland.conf)
local mainMod = "SUPER"
-- Move focus with mainMod + arrow keys
hl.bind(mainMod .. " + left", hl.dsp.focus({ direction = "left" }))
hl.bind(mainMod .. " + right", hl.dsp.focus({ direction = "right" }))
hl.bind(mainMod .. " + up", hl.dsp.focus({ direction = "up" }))
hl.bind(mainMod .. " + down", hl.dsp.focus({ direction = "down" }))
-- Switch workspaces with mainMod + [0-9]
for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }))
end
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
for i = 1, 10 do
local key = i % 10
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- Example special workspace (scratchpad)
hl.bind(mainMod .. " + S", hl.dsp.workspace.toggle_special())
hl.bind(mainMod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special" }))
-- Scroll through existing workspaces with mainMod + scroll
hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
hl.bind("CTRL + ALT + right", hl.dsp.focus({ workspace = "e+1" }))
hl.bind("CTRL + ALT + left", hl.dsp.focus({ workspace = "e-1" }))
-- Move/resize windows with mainMod + LMB/RMB and dragging
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- Swap active window with the one next to it with SUPER + SHIFT + arrow keys
hl.bind("SUPER + SHIFT + left", hl.dsp.window.swap({ direction = "left" }), { description = "Swap window to the left" })
hl.bind("SUPER + SHIFT + right", hl.dsp.window.swap({ direction = "right" }), { description = "Swap window to the right" })
hl.bind("SUPER + SHIFT + up", hl.dsp.window.swap({ direction = "up" }), { description = "Swap window up" })
hl.bind("SUPER + SHIFT + down", hl.dsp.window.swap({ direction = "down" }), { description = "Swap window down" })
-- Laptop multimedia keys for volume and LCD brightness (bindel = locked + repeating)
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-volume.sh up"), { locked = true, repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-volume.sh down"), { locked = true, repeating = true })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true, repeating = true })
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"),{ locked = true, repeating = true })
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-brightness.sh up"), { locked = true, repeating = true })
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("~/.config/hypr/scripts/wob-brightness.sh down"), { locked = true, repeating = true })
-- Requires playerctl (bindl = locked, no repeat)
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
require("keybindings")
---------------------------
--## WINDOWS AND WORKSPACES ###
---------------------------
hl.window_rule({
name = "suppress-maximize",
match = { class = ".*" },
suppress_event = "maximize",
})
hl.window_rule({
name = "xwayland-nofocus-fix",
match = {
class = "^$",
title = "^$",
xwayland = 1,
float = 1,
fullscreen = 0,
pin = 0,
},
no_focus = true,
})
require("windowrule")
-- Host overrides (detect machine by hostname and load matching hosts/<host>.lua)
local hostname = (io.popen("hostname") or {}):read("*l") or ""
hostname = hostname:gsub("%..*$", "") -- strip domain suffix
local ok, err = pcall(require, "hosts." .. hostname)
if not ok then
-- No host file matches this machine; use the defaults already set above.
-- See hosts/example.lua for a template on adding your own host.
end