qt6: switch from kvantum to kde family & hyprland: a new set of config

This commit is contained in:
2026-08-21 20:02:28 +02:00
parent 3082b15023
commit c53c27c5e3
198 changed files with 17376 additions and 7631 deletions
+5
View File
@@ -0,0 +1,5 @@
{
"workspace": {
"library": ["/usr/share/hypr/stubs"]
}
}
+16
View File
@@ -0,0 +1,16 @@
-- https://wiki.hypr.land/Configuring/Start/
-- require("hyprland/envs") -- managed by uwsm
require("hyprland/monitors")
require("hyprland/permissions")
require("hyprland/input")
require("hyprland/looks")
require("hyprland/animations")
require("hyprland/layouts")
require("hyprland/rules")
require("hyprland/gestures")
require("hyprland/binds")
require("hyprland/execs")
+45
View File
@@ -0,0 +1,45 @@
-- Windows use springs so they settle with weight; everything else uses short
-- asymmetric beziers, entering with deceleration and leaving faster.
hl.curve("standard", { type = "bezier", points = { { 0.2, 0.0 }, { 0.0, 1.0 } } })
hl.curve("decel", { type = "bezier", points = { { 0.05, 0.7 }, { 0.1, 1.0 } } })
hl.curve("accel", { type = "bezier", points = { { 0.3, 0.0 }, { 0.8, 0.15 } } })
hl.curve("linear", { type = "bezier", points = { { 0.0, 0.0 }, { 1.0, 1.0 } } })
-- Stiffness sets the speed, dampening how much it overshoots.
hl.curve("snappy", { type = "spring", mass = 1, stiffness = 300, dampening = 26 })
hl.curve("smooth", { type = "spring", mass = 1, stiffness = 190, dampening = 24 })
hl.curve("lively", { type = "spring", mass = 1, stiffness = 250, dampening = 19 })
-- Unset leaves inherit from their parent.
hl.animation({ leaf = "global", enabled = true, speed = 4, bezier = "standard" })
hl.animation({ leaf = "windows", enabled = true, speed = 4, spring = "snappy" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4, spring = "lively", style = "popin 92%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 2.2, bezier = "accel", style = "popin 94%" })
hl.animation({ leaf = "windowsMove", enabled = true, speed = 4, spring = "smooth" })
hl.animation({ leaf = "layers", enabled = true, speed = 3, bezier = "decel" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 3, bezier = "decel", style = "slide" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 2.2, bezier = "accel", style = "slide" })
hl.animation({ leaf = "fade", enabled = true, speed = 2.6, bezier = "standard" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 2.2, bezier = "decel" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.8, bezier = "accel" })
hl.animation({ leaf = "fadeSwitch", enabled = true, speed = 2.5, bezier = "standard" })
hl.animation({ leaf = "fadeDim", enabled = true, speed = 2.5, bezier = "standard" })
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 2.2, bezier = "decel" })
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.8, bezier = "accel" })
hl.animation({ leaf = "fadePopupsIn", enabled = true, speed = 1.8, bezier = "decel" })
hl.animation({ leaf = "fadePopupsOut", enabled = true, speed = 1.4, bezier = "accel" })
hl.animation({ leaf = "border", enabled = true, speed = 3, bezier = "standard" })
-- `borderangle` with style "loop" is left unset on purpose: it renders
-- continuously at the refresh rate even when idle, which costs battery.
hl.animation({ leaf = "workspaces", enabled = true, speed = 4.5, bezier = "standard", style = "slidefadevert 12%" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 4, spring = "smooth", style = "slidevert" })
hl.animation({ leaf = "zoomFactor", enabled = true, speed = 4, bezier = "decel" })
hl.animation({ leaf = "monitorAdded", enabled = true, speed = 4, bezier = "decel" })
+189
View File
@@ -0,0 +1,189 @@
local mod = "SUPER"
local L = require("hyprland/layout-actions")
-- Applications
hl.bind(mod .. " + T", hl.dsp.exec_cmd("kitty"), { description = "Terminal" })
hl.bind(mod .. " + Return", hl.dsp.exec_cmd("kitty"), { description = "Terminal" })
hl.bind(mod .. " + SHIFT + T", hl.dsp.exec_cmd("kitty-floating"), { description = "Floating terminal" })
hl.bind(mod .. " + SHIFT + Return", hl.dsp.exec_cmd("kitty-floating"), { description = "Floating terminal" })
hl.bind(mod .. " + C", hl.dsp.exec_cmd("code"), { description = "Editor" })
hl.bind(mod .. " + E", hl.dsp.exec_cmd("nautilus --new-window"), { description = "File manager (GTK)" })
hl.bind(mod .. " + SHIFT + E", hl.dsp.exec_cmd("dolphin --new-window"), { description = "File manager" })
hl.bind(mod .. " + W", hl.dsp.exec_cmd("zen || zen-browser"), { description = "Browser" })
hl.bind(mod .. " + B", hl.dsp.exec_cmd("pkill -x -n btop || kitty-floating -e btop"), { description = "System monitor" })
hl.bind(mod .. " + O", hl.dsp.exec_cmd("pkill -x -n pwvucontrol || pwvucontrol"), { description = "Audio mixer" })
hl.bind("CTRL + ALT + Delete", hl.dsp.exec_cmd("pkill -x wlogout || wlogout"), { description = "Session menu" })
-- Launcher. URLs are quoted: `?` is a glob to the `sh -c` exec_cmd runs.
hl.bind("ALT + space", hl.dsp.exec_cmd("vicinae toggle"), { description = "Launcher" })
hl.bind(mod .. " + D", hl.dsp.exec_cmd("vicinae 'vicinae://launch/system/run?toggle=true'"), { description = "Run command" })
hl.bind(mod .. " + V", hl.dsp.exec_cmd("vicinae 'vicinae://launch/clipboard/history?toggle=true'"), { description = "Clipboard history" })
hl.bind(mod .. " + period", hl.dsp.exec_cmd("vicinae 'vicinae://launch/core/search-emojis?toggle=true'"), { description = "Emoji picker" })
-- Screenshots and colour
hl.bind("Print", hl.dsp.exec_cmd("screenshot-script full"), { description = "Screenshot: screen" })
hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("screenshot-script area"), { description = "Screenshot: region" })
hl.bind(mod .. " + CTRL + SHIFT + S", hl.dsp.exec_cmd("screenshot-script window"), { description = "Screenshot: window" })
hl.bind(mod .. " + SHIFT + C", hl.dsp.exec_cmd("hyprpicker -a"), { description = "Pick colour" })
-- Utilities
hl.bind(mod .. " + N", hl.dsp.exec_cmd("sunset"), { description = "Night light" })
hl.bind(mod .. " + SHIFT + K", hl.dsp.exec_cmd("pkill -x waybar; waybar"), { description = "Restart the bar" })
hl.bind(mod .. " + CTRL + R", hl.dsp.exec_cmd("hyprctl reload"), { description = "Reload config" })
-- hypridle has no runtime toggle, and probing for it would mean a blocking
-- popen, which must not happen in a bind callback.
local idle_inhibited = false
hl.bind(mod .. " + I", function()
idle_inhibited = not idle_inhibited
if idle_inhibited then
hl.exec_cmd("pkill -x hypridle")
else
hl.exec_cmd("hypridle")
end
hl.notification.create({
text = idle_inhibited and "Idle inhibited" or "Idle timers active",
timeout = 1500,
})
end, { description = "Toggle idle inhibit" })
-- Media and brightness. BRIGHTNESSCTL_DEVICE comes from set_display.
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"),
{ locked = true, repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"),
{ locked = true, repeating = true })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true })
hl.bind("XF86AudioMicMute", 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("XF86AudioPause", 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("XF86MonBrightnessUp",
hl.dsp.exec_cmd('brightnessctl -d "${BRIGHTNESSCTL_DEVICE:-intel_backlight}" -e4 -n2 set 5%+'),
{ locked = true, repeating = true })
hl.bind("XF86MonBrightnessDown",
hl.dsp.exec_cmd('brightnessctl -d "${BRIGHTNESSCTL_DEVICE:-intel_backlight}" -e4 -n2 set 5%-'),
{ locked = true, repeating = true })
-- Window management
hl.bind(mod .. " + Q", hl.dsp.window.close(), { description = "Close window" })
hl.bind("ALT + F4", hl.dsp.window.close(), { description = "Close window" })
hl.bind(mod .. " + SHIFT + Q", hl.dsp.window.kill(), { description = "Force kill window" })
for key, dir in pairs({ Left = "l", Right = "r", Up = "u", Down = "d" }) do
hl.bind(mod .. " + " .. key, hl.dsp.focus({ direction = dir }))
hl.bind(mod .. " + SHIFT + " .. key, hl.dsp.window.move({ direction = dir, group_aware = true }))
hl.bind(mod .. " + CTRL + " .. key, hl.dsp.focus({ monitor = dir }))
hl.bind(mod .. " + CTRL + SHIFT + " .. key, hl.dsp.window.move({ monitor = dir, follow = true }))
end
hl.bind(mod .. " + ALT + space", hl.dsp.window.float({ action = "toggle" }), { description = "Toggle floating" })
hl.bind(mod .. " + P", hl.dsp.window.pin(), { description = "Pin window" })
hl.bind(mod .. " + Y", hl.dsp.window.center(), { description = "Centre window" })
hl.bind(mod .. " + F", hl.dsp.window.fullscreen({ mode = "maximized" }), { description = "Maximize" })
hl.bind(mod .. " + SHIFT + F", hl.dsp.window.fullscreen({ mode = "fullscreen" }), { description = "Fullscreen" })
-- The client believes it is fullscreen, the layout does not. Stops
-- Chromium-likes dropping into presentation mode.
hl.bind(mod .. " + ALT + F", hl.dsp.window.fullscreen_state({ internal = 0, client = 2 }),
{ description = "Fake fullscreen" })
hl.bind("ALT + Tab", function()
L.cycle_next()
hl.dispatch(hl.dsp.window.bring_to_top())
end, { description = "Next window" })
hl.bind("ALT + SHIFT + Tab", function()
L.cycle_prev()
hl.dispatch(hl.dsp.window.bring_to_top())
end, { description = "Previous window" })
hl.bind(mod .. " + SHIFT + Tab", function()
local w = hl.get_active_window()
hl.dispatch(hl.dsp.focus({ window = (w and w.floating) and "tiled" or "floating" }))
end, { description = "Floating <-> tiled focus" })
-- Tabbed groups
hl.bind(mod .. " + SHIFT + M", hl.dsp.group.toggle(), { description = "Toggle tabbed group" })
hl.bind(mod .. " + CTRL + Tab", hl.dsp.group.next(), { description = "Next tab" })
hl.bind(mod .. " + CTRL + SHIFT + Tab", hl.dsp.group.prev(), { description = "Previous tab" })
-- Layout-aware
hl.bind(mod .. " + minus", L.narrow, { repeating = true, description = "Narrower" })
hl.bind(mod .. " + plus", L.widen, { repeating = true, description = "Wider" })
hl.bind(mod .. " + SHIFT + minus", hl.dsp.window.resize({ x = 0, y = -40, relative = true }), { repeating = true })
hl.bind(mod .. " + SHIFT + plus", hl.dsp.window.resize({ x = 0, y = 40, relative = true }), { repeating = true })
hl.bind(mod .. " + R", L.cycle_width, { description = "Cycle preset width" })
hl.bind(mod .. " + J", L.tweak, { description = "Split / orientation / expand" })
hl.bind(mod .. " + U", L.promote, { description = "Promote window" })
hl.bind(mod .. " + ALT + Left", L.consume_prev, { description = "Consume/expel left" })
hl.bind(mod .. " + ALT + Right", L.consume_next, { description = "Consume/expel right" })
hl.bind(mod .. " + SHIFT + comma", L.consume, { description = "Consume window" })
hl.bind(mod .. " + SHIFT + period", L.expel, { description = "Expel window" })
-- Workspaces
for i = 1, 10 do
local key = tostring(i % 10) -- workspace 10 sits on the 0 key
hl.bind(mod .. " + " .. key, hl.dsp.focus({ workspace = i }))
hl.bind(mod .. " + ALT + " .. key, hl.dsp.window.move({ workspace = i, follow = true }))
end
hl.bind(mod .. " + Page_Down", hl.dsp.focus({ workspace = "+1" }))
hl.bind(mod .. " + Page_Up", hl.dsp.focus({ workspace = "-1" }))
-- hl.bind(mod .. " + Down", hl.dsp.focus({ workspace = "+1" }))
-- hl.bind(mod .. " + Up", hl.dsp.focus({ workspace = "-1" }))
hl.bind(mod .. " + SHIFT + Page_Down", hl.dsp.window.move({ workspace = "+1", follow = true }))
hl.bind(mod .. " + SHIFT + Page_Up", hl.dsp.window.move({ workspace = "-1", follow = true }))
-- hl.bind(mod .. " + SHIFT + Down", hl.dsp.window.move({ workspace = "+1", follow = true }))
-- hl.bind(mod .. " + SHIFT + Up", hl.dsp.window.move({ workspace = "-1", follow = true }))
-- Scroll through only the workspaces that exist.
hl.bind(mod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
hl.bind(mod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
hl.bind(mod .. " + Tab", hl.dsp.focus({ workspace = "previous" }), { description = "Last workspace" })
hl.bind(mod .. " + S", hl.dsp.workspace.toggle_special("scratchpad"), { description = "Scratchpad" })
hl.bind(mod .. " + ALT + S", hl.dsp.window.move({ workspace = "special:scratchpad" }),
{ description = "Send to scratchpad" })
-- Mouse
hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
hl.bind(mod .. " + mouse:274", hl.dsp.window.close(), { description = "Close window" })
-- Cycles dwindle -> master -> scrolling, notifying which. No submap: a modal
-- state with no on-screen indicator traps the keyboard.
hl.bind(mod .. " + SHIFT + L", L.next(), { description = "Cycle layout" })
-- Session
hl.bind(mod .. " + L", hl.dsp.exec_cmd("loginctl lock-session"), { description = "Lock" })
-- The delay is deliberate: driving dpms straight from a keybind is documented
-- as undefined behaviour.
hl.bind(mod .. " + SHIFT + P", function()
hl.exec_cmd("loginctl lock-session")
hl.timer(function()
hl.dispatch(hl.dsp.dpms({ action = "disable" }))
end, { timeout = 800, type = "oneshot" })
end, { description = "Lock and blank screens" })
-- hyprshutdown rather than hl.dsp.exit(), so the session tears down in order.
hl.bind(mod .. " + K", hl.dsp.exec_cmd("hyprshutdown"), { description = "Exit Hyprland" })
+41
View File
@@ -0,0 +1,41 @@
-- Cursor
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
hl.env("XCURSOR_THEME", "Bibata-Modern-Ice")
-- Qt
hl.env("QT_QPA_PLATFORM", "wayland")
hl.env("QT_QPA_PLATFORMTHEME", "kde")
hl.env("QT_STYLE_OVERRIDE", "Kvantum")
-- Nvidia
hl.env("LIBVA_DRIVER_NAME", "nvidia")
hl.env("NVD_BACKEND", "nvidia")
hl.env("GBM_BACKEND", "nvidia-drm")
-- Aquamarine reads AQ_DRM_DEVICES; set_display exports the resolved list as
-- HYPR_AQ_DRM_DEVICES. The fallback applies only if it was never sourced.
local drm_devices = os.getenv("HYPR_AQ_DRM_DEVICES")
if drm_devices == nil or drm_devices == "" then
drm_devices = "/dev/dri/card0:/dev/dri/card1"
end
hl.env("AQ_DRM_DEVICES", drm_devices)
-- Input method
hl.env("QT_IM_MODULE", "fcitx")
hl.env("XMODIFIERS", "@im=fcitx")
hl.env("SDL_IM_MODULE", "fcitx")
hl.env("GLFW_IM_MODULE", "ibus")
hl.env("INPUT_METHOD", "fcitx")
-- Swing
hl.env("_JAVA_AWT_WM_NONREPARENTING", "1")
-- Others
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "wayland")
+10
View File
@@ -0,0 +1,10 @@
hl.on("hyprland.start", function()
hl.exec_cmd("uwsm finalize HYPRLAND_INSTANCE_SIGNATURE")
hl.exec_cmd("waybar")
hl.exec_cmd("gnome-keyring-daemon --start --components=secrets")
hl.exec_cmd("/usr/lib/soteria-polkit/soteria")
hl.exec_cmd("solaar -w hide")
hl.exec_cmd("sunshine")
hl.exec_cmd("hypridle")
hl.exec_cmd("vicinae server")
end)
+28
View File
@@ -0,0 +1,28 @@
hl.config({
gestures = {
workspace_swipe_distance = 400,
workspace_swipe_cancel_ratio = 0.2,
workspace_swipe_min_speed_to_force = 5,
workspace_swipe_direction_lock = true,
workspace_swipe_direction_lock_threshold = 10,
workspace_swipe_create_new = true,
workspace_swipe_forever = true,
workspace_swipe_touch = false,
},
})
hl.gesture({ fingers = 3, direction = "vertical", action = "workspace" })
-- Only does anything under the scrolling layout, harmless elsewhere.
hl.gesture({ fingers = 4, direction = "horizontal", action = "scroll_move" })
hl.gesture({ fingers = 4, direction = "up", action = "special", workspace_name = "scratchpad" })
-- Held modifiers turn a swipe into a window action rather than navigation.
hl.gesture({ fingers = 3, direction = "up", mods = "SUPER", action = "fullscreen" })
hl.gesture({ fingers = 3, direction = "down", mods = "SUPER", action = "close" })
hl.gesture({ fingers = 3, direction = "left", mods = "SUPER", scale = 1.5, action = "float" })
hl.gesture({ fingers = 2, direction = "pinch", action = "cursor_zoom", zoom_level = 1, mode = "live" })
+14
View File
@@ -0,0 +1,14 @@
hl.config({
input = {
kb_layout = "de",
numlock_by_default = true,
follow_mouse = 1,
sensitivity = 0, -- -1.0 - 1.0
touchpad = {
natural_scroll = true,
},
},
})
+120
View File
@@ -0,0 +1,120 @@
-- Layout messages are layout-specific: "splitratio" means nothing to master,
-- "mfact" nothing to scrolling. These resolve one key to the right action.
local M = {}
--- Layout of the active workspace; per-workspace rules win over the default.
function M.current()
local ws = hl.get_active_workspace()
if ws and ws.tiled_layout and ws.tiled_layout ~= "" then
return ws.tiled_layout
end
return hl.get_config("general.layout") or "dwindle"
end
--- `map` is keyed by layout name, `map.default` covers anything unlisted.
--- Values are functions so no dispatcher is built until the bind fires.
local function by_layout(map)
return function()
local pick = map[M.current()] or map.default
if pick then
hl.dispatch(pick())
end
end
end
M.by_layout = by_layout
M.widen = by_layout({
dwindle = function() return hl.dsp.layout("splitratio +0.05") end,
master = function() return hl.dsp.layout("mfact +0.05") end,
scrolling = function() return hl.dsp.layout("colresize +0.05") end,
})
M.narrow = by_layout({
dwindle = function() return hl.dsp.layout("splitratio -0.05") end,
master = function() return hl.dsp.layout("mfact -0.05") end,
scrolling = function() return hl.dsp.layout("colresize -0.05") end,
})
--- Cycle preset widths under scrolling, elsewhere snap back to an even split.
M.cycle_width = by_layout({
dwindle = function() return hl.dsp.layout("splitratio 1.0 exact") end,
master = function() return hl.dsp.layout("mfact exact 0.58") end,
scrolling = function() return hl.dsp.layout("colresize +conf") end,
})
--- Root of the tree, the master slot, or a column of its own.
M.promote = by_layout({
dwindle = function() return hl.dsp.layout("movetoroot") end,
master = function() return hl.dsp.layout("swapwithmaster master") end,
scrolling = function() return hl.dsp.layout("promote") end,
})
--- Flip the split, rotate the master area, or fill the free space.
M.tweak = by_layout({
dwindle = function() return hl.dsp.layout("togglesplit") end,
master = function() return hl.dsp.layout("orientationnext") end,
scrolling = function() return hl.dsp.layout("fit expand") end,
})
M.cycle_next = by_layout({
master = function() return hl.dsp.layout("cyclenext") end,
default = function() return hl.dsp.window.cycle_next() end,
})
M.cycle_prev = by_layout({
master = function() return hl.dsp.layout("cycleprev") end,
default = function() return hl.dsp.window.cycle_next({ next = false }) end,
})
--- Scrolling consumes into columns; elsewhere the analogue is a tabbed group.
M.consume_prev = by_layout({
scrolling = function() return hl.dsp.layout("consume_or_expel prev") end,
default = function() return hl.dsp.window.move({ into_or_create_group = "l" }) end,
})
M.consume_next = by_layout({
scrolling = function() return hl.dsp.layout("consume_or_expel next") end,
default = function() return hl.dsp.window.move({ into_or_create_group = "r" }) end,
})
M.consume = by_layout({
scrolling = function() return hl.dsp.layout("consume") end,
default = function() return hl.dsp.window.move({ into_or_create_group = "l" }) end,
})
M.expel = by_layout({
scrolling = function() return hl.dsp.layout("expel") end,
default = function() return hl.dsp.window.move({ out_of_group = true }) end,
})
local LAYOUTS = { "dwindle", "master", "scrolling" }
--- Re-point the active workspace at another layout. Returns a bind callback.
function M.set(name)
return function()
local ws = hl.get_active_workspace()
if not ws then
return
end
hl.workspace_rule({ workspace = tostring(ws.id), layout = name })
hl.notification.create({ text = "Layout: " .. name, timeout = 1500 })
end
end
function M.next()
return function()
local now = M.current()
local at = 1
for i, name in ipairs(LAYOUTS) do
if name == now then
at = i
break
end
end
M.set(LAYOUTS[(at % #LAYOUTS) + 1])()
end
end
return M
+58
View File
@@ -0,0 +1,58 @@
-- All three layouts. SUPER+SHIFT+L switches the active workspace between them;
-- the keymap is shared, see layout-actions.lua.
hl.config({
dwindle = {
-- Required for SUPER+J (togglesplit) to mean anything.
preserve_split = true,
smart_split = false,
force_split = 2, -- new window lands right/bottom
smart_resizing = true,
use_active_for_splits = true,
precise_mouse_move = true,
default_split_ratio = 1.0,
-- 2560px stays "wide" through several splits, so stack sooner.
split_width_multiplier = 1.35,
special_scale_factor = 1.0,
},
master = {
mfact = 0.58,
orientation = "left",
-- New windows join the stack instead of stealing the master slot.
new_status = "slave",
new_on_active = "after",
new_on_top = false,
allow_small_split = true,
smart_resizing = true,
drop_at_cursor = true,
always_keep_position = false,
focus_master_on_close = false,
slave_count_for_center_master = 2,
center_master_fallback = "left",
special_scale_factor = 1.0,
},
scrolling = {
column_width = 0.5,
explicit_column_widths = "0.3, 0.5, 0.7, 1.0",
fullscreen_on_one_column = true,
focus_fit_method = 1,
follow_focus = true,
follow_min_visible = 0.4,
-- Let focus fall off the ends onto the other monitor.
wrap_focus = false,
wrap_swapcol = true,
direction = "right",
},
})
+168
View File
@@ -0,0 +1,168 @@
local c = require("hyprland/theme")
hl.config({
general = {
gaps_in = 3,
gaps_out = 6,
gaps_workspaces = 40,
border_size = 2,
col = {
active_border = c.gradient(c.blue, c.mauve, 45),
inactive_border = c.rgba(c.surface0, "cc"),
},
resize_on_border = true,
extend_border_grab_area = 12, -- 2px borders are hard to hit
hover_icon_on_border = true,
no_focus_fallback = true,
allow_tearing = true, -- gates the `immediate` rule
layout = "dwindle",
snap = {
enabled = true,
window_gap = 8,
monitor_gap = 8,
respect_gaps = true,
},
},
decoration = {
rounding = 14,
rounding_power = 2,
active_opacity = 1.0,
inactive_opacity = 0.96,
fullscreen_opacity = 1.0,
dim_inactive = false,
dim_special = 0.3,
shadow = {
enabled = true,
range = 24,
render_power = 3,
offset = { 0, 4 },
color = c.rgba("000000", "44"),
color_inactive = c.rgba("000000", "22"),
},
blur = {
enabled = true,
size = 6,
passes = 3,
new_optimizations = true,
xray = true,
ignore_opacity = true,
noise = 0.012,
contrast = 1.0,
brightness = 1.0,
vibrancy = 0.17,
vibrancy_darkness = 0.0,
special = false,
popups = true,
popups_ignorealpha = 0.6,
-- Keeps the fcitx5 candidate window readable over bright content.
input_methods = true,
input_methods_ignorealpha = 0.8,
},
},
group = {
auto_group = false,
insert_after_current = true,
focus_removed_window = true,
merge_groups_on_drag = true,
col = {
border_active = c.gradient(c.blue, c.mauve, 45),
border_inactive = c.rgba(c.surface0, "cc"),
border_locked_active = c.gradient(c.peach, c.red, 45),
border_locked_inactive = c.rgba(c.surface0, "cc"),
},
groupbar = {
enabled = true,
font_family = "Noto Sans",
font_size = 11,
height = 18,
indicator_height = 3,
indicator_gap = 2,
gradients = false,
render_titles = true,
scrolling = true,
middle_click_close = true,
stacked = false,
keep_upper_gap = true,
rounding = 6,
gaps_in = 2,
gaps_out = 2,
col = {
active = c.rgb(c.blue),
inactive = c.rgba(c.surface0, "dd"),
locked_active = c.rgb(c.peach),
locked_inactive = c.rgba(c.surface0, "dd"),
},
text_color = c.rgb(c.crust),
text_color_inactive = c.rgb(c.text),
},
},
cursor = {
warp_on_change_workspace = 1,
warp_on_toggle_special = 1,
persistent_warps = true,
hide_on_key_press = true,
inactive_timeout = 0,
default_monitor = "DP-1",
},
binds = {
workspace_back_and_forth = true,
allow_workspace_cycles = true,
hide_special_on_workspace_change = true,
movefocus_cycles_fullscreen = false,
window_direction_monitor_fallback = true,
scroll_event_delay = 0,
drag_threshold = 10,
},
misc = {
font_family = "Noto Sans",
-- Backdrop behind windows; no wallpaper daemon is started here.
disable_hyprland_logo = true,
disable_splash_rendering = true,
force_default_wallpaper = 0,
background_color = c.rgb(c.base),
vrr = 2, -- fullscreen only; 3 also gates on content type
mouse_move_enables_dpms = true,
key_press_enables_dpms = true,
animate_manual_resizes = false,
animate_mouse_windowdragging = false,
enable_swallow = false,
middle_click_paste = false,
focus_on_activate = true,
initial_workspace_tracking = 0,
on_focus_under_fullscreen = 2,
allow_session_lock_restore = true,
close_special_on_empty = true,
},
xwayland = {
-- The panel runs at 1.25; without this XWayland apps render blurry.
force_zero_scaling = true,
},
})
+27
View File
@@ -0,0 +1,27 @@
hl.monitor({
output = "eDP-1",
mode = "2560x1600@60",
position = "0x0",
scale = 1.25
})
hl.monitor({
output = "eDP-2",
mode = "2560x1600@60",
position = "0x0",
scale = 1.25
})
hl.monitor({
output = "DP-1",
mode = "2560x1440@180",
position = "2048x0",
scale = 1
})
hl.workspace_rule({ workspace = 1, monitor = "DP-1", persistent = true })
hl.workspace_rule({ workspace = 3, monitor = "DP-1", persistent = false })
hl.workspace_rule({ workspace = 5, monitor = "DP-1", persistent = false })
hl.workspace_rule({ workspace = 2, monitor = "eDP-1", persistent = true })
hl.workspace_rule({ workspace = 4, monitor = "eDP-1", persistent = false })
hl.workspace_rule({ workspace = 6, monitor = "eDP-1", persistent = false })
+17
View File
@@ -0,0 +1,17 @@
hl.config({
ecosystem = {
enforce_permissions = true,
},
})
hl.permission({ binary = "/usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland", type = "screencopy", mode = "allow" })
hl.permission({ binary = "/usr/(bin|local/bin)/hyprpm", type = "plugin", mode = "allow" })
-- Prompts are drawn by hyprland-qtutils, which is not installed, so anything
-- not granted here fails rather than asking.
hl.permission({ binary = "/usr/(bin|local/bin)/grim", type = "screencopy", mode = "allow" })
hl.permission({ binary = "/usr/(bin|local/bin)/hyprshot", type = "screencopy", mode = "allow" })
hl.permission({ binary = "/usr/(bin|local/bin)/hyprpicker", type = "screencopy", mode = "allow" })
hl.permission({ binary = "/usr/(bin|local/bin)/wf-recorder", type = "screencopy", mode = "allow" })
hl.permission({ binary = "/usr/(bin|local/bin)/wl-mirror", type = "screencopy", mode = "allow" })
hl.permission({ binary = "/usr/(bin|local/bin)/sunshine", type = "screencopy", mode = "allow" })
+140
View File
@@ -0,0 +1,140 @@
local c = require("hyprland/theme")
-- Most apps ask to be maximized on launch, which is rarely wanted when tiling.
hl.window_rule({
name = "suppress-maximize",
match = { class = ".*" },
suppress_event = "maximize",
})
-- XWayland drag surfaces appear as empty floating windows; focusing them
-- breaks the drag.
hl.window_rule({
name = "fix-xwayland-drags",
match = { class = "^$", title = "^$", xwayland = true, float = true, fullscreen = false, pin = false },
no_focus = true,
})
-- Blurring XWayland makes their client-drawn shadows look wrong.
hl.window_rule({ name = "no-blur-xwayland", match = { xwayland = true }, no_blur = true })
hl.window_rule({ name = "no-shadow-tiled", match = { float = false }, no_shadow = true })
-- Dialogs
local dialogs = {
"^(Open File)(.*)$",
"^(Select a File)(.*)$",
"^(Choose wallpaper)(.*)$",
"^(Open Folder)(.*)$",
"^(Save As)(.*)$",
"^(Library)(.*)$",
"^(File Upload)(.*)$",
}
for _, title in ipairs(dialogs) do
hl.window_rule({ match = { title = title }, float = true, center = true })
end
hl.window_rule({ name = "float-modals", match = { modal = true }, float = true, center = true })
-- Picture-in-Picture
local pip = "^([Pp]icture[-\\s]?[Ii]n[-\\s]?[Pp]icture)(.*)$"
hl.window_rule({
name = "pip",
match = { title = pip },
float = true,
pin = true,
keep_aspect_ratio = true,
size = { "monitor_w*0.22", "monitor_h*0.22" },
move = { "monitor_w-window_w-24", "monitor_h-window_h-24" },
no_shadow = false,
})
-- Floating by default
local floaters = {
"^(blueberry\\.py)$",
"^(blueman-manager)$",
"^(org\\.pulseaudio\\.pavucontrol)$",
"^(com\\.saivert\\.pwvucontrol)$",
"^(Waydroid)$",
"^(waydroid.*)$",
"^(org\\.kde\\.kcalc)$",
"^(org\\.kde\\.kalk)$",
"^(org\\.gnome\\.NautilusPreviewer)$",
"^(coin)$",
"^(wallreel)$",
"^(be\\.alexandervanhee\\.gradia)$",
"^(nm-connection-editor)$",
"^(org\\.gnome\\.Calculator)$",
"^(xdg-desktop-portal-gtk)$",
}
for _, class in ipairs(floaters) do
hl.window_rule({ match = { class = class }, float = true, center = true })
end
-- QQ's image viewer has no useful class, only a title.
hl.window_rule({ match = { title = "^(图片查看器)(.*)$" }, float = true, center = true })
-- Terminals
hl.window_rule({
name = "floating-terminal",
match = { class = "^(kitty-floating)$" },
float = true,
size = { "monitor_w*0.5", "monitor_h*0.5" },
center = true,
})
hl.window_rule({ match = { class = "^(com\\.mitchellh\\.ghostty)$" }, float = true, center = true })
hl.window_rule({ match = { class = "^(kitty)$" }, scrolling_width = 0.5 })
hl.window_rule({ match = { class = "^(scrcpy)$" }, scrolling_width = 0.3 })
-- Media, games, tearing
hl.window_rule({ name = "opaque-video", match = { content = "video" }, opaque = true, no_blur = true })
hl.window_rule({
name = "opaque-game",
match = { content = "game" },
opaque = true,
no_blur = true,
no_dim = true,
immediate = true,
})
hl.window_rule({ match = { class = "^(steam_app.*)$" }, immediate = true })
hl.window_rule({ match = { title = "^(.*\\.exe)$" }, immediate = true })
-- Steam's notification toasts are windows, and they steal focus.
hl.window_rule({
name = "steam-toasts",
match = { class = "^(steam)$", title = "^(notificationtoasts_\\d+_desktop)$" },
float = true,
no_initial_focus = true,
no_shadow = true,
})
hl.window_rule({ match = { class = "^(org\\.mozilla\\.[Tt]hunderbird)$" }, no_screen_share = true })
hl.window_rule({ match = { pin = true }, border_color = c.gradient(c.teal, c.green, 45) })
-- Smart gaps: a lone tiled or fullscreen window drops its gaps and border.
-- Rounding is kept.
hl.workspace_rule({ workspace = "w[tv1]s[false]", gaps_out = 0, gaps_in = 0 })
hl.workspace_rule({ workspace = "f[1]s[false]", gaps_out = 0, gaps_in = 0 })
hl.window_rule({ match = { float = false, workspace = "w[tv1]s[false]" }, border_size = 0 })
hl.window_rule({ match = { float = false, workspace = "f[1]s[false]" }, border_size = 0 })
hl.workspace_rule({ workspace = "special:scratchpad", gaps_out = 40, gaps_in = 6 })
-- Layer rules
hl.layer_rule({ name = "blur-bar", match = { namespace = "^waybar$" }, blur = true, ignore_alpha = 0.4 })
hl.layer_rule({ name = "blur-launcher", match = { namespace = "^vicinae$" }, blur = true, ignore_alpha = 0.4 })
hl.layer_rule({ name = "blur-logout", match = { namespace = "^logout_dialog$" }, blur = true })
hl.layer_rule({ name = "blur-notifications", match = { namespace = "^notifications$" }, blur = true, ignore_alpha = 0.6 })
-- The colour picker must see the screen exactly as it is.
hl.layer_rule({ name = "no-anim-picker", match = { namespace = "^hyprpicker$" }, no_anim = true })
hl.layer_rule({ name = "no-anim-selection", match = { namespace = "^selection$" }, no_anim = true })
+37
View File
@@ -0,0 +1,37 @@
-- Catppuccin Mocha.
local M = {
base = "1e1e2e",
mantle = "181825",
crust = "11111b",
surface0 = "313244",
surface1 = "45475a",
surface2 = "585b70",
overlay0 = "6c7086",
subtext0 = "a6adc8",
text = "cdd6f4",
blue = "89b4fa",
lavender = "b4befe",
mauve = "cba6f7",
red = "f38ba8",
peach = "fab387",
yellow = "f9e2af",
green = "a6e3a1",
teal = "94e2d5",
}
function M.rgb(hex)
return "rgb(" .. hex .. ")"
end
--- `alpha` is a two-digit hex string, e.g. "80".
function M.rgba(hex, alpha)
return "rgba(" .. hex .. alpha .. ")"
end
function M.gradient(from, to, angle)
return { colors = { M.rgb(from), M.rgb(to) }, angle = angle or 45 }
end
return M