All checks were successful
Mirror to GitHub / mirror (push) Successful in 6s
Best practice for the long-running bread daemon: ship an enabled user unit in skel (~/.config/systemd/user/breadd.service + default.target.wants symlink) instead of a bare Hyprland exec-once. Gives crash-restart, journald logging (journalctl --user -u breadd), and proper lifecycle. - ExecStart uses %h so it works for any account created from skel (not a hardcoded home). - RuntimeDirectoryPreserve=yes so restarting breadd doesn't wipe the shared theme.css that bread-theme writes into /run/user/<uid>/bread. - hyprland.lua: replace the `breadd` exec-once with a Wayland-env import (dbus-update-activation-environment) + `systemctl --user restart breadd`, so the service — which autostarts at login before Hyprland exists — picks up HYPRLAND_INSTANCE_SIGNATURE and can drive the compositor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
221 lines
12 KiB
Lua
221 lines
12 KiB
Lua
-- BOS Hyprland configuration — native Lua config (Hyprland 0.55+).
|
||
-- hyprlang (.conf) is deprecated; this uses the built-in `hl` API.
|
||
-- Single-file and non-modular by design. Reference: https://wiki.hypr.land/
|
||
|
||
local mod = "SUPER"
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Monitors — generic default that works on any hardware.
|
||
-- ---------------------------------------------------------------------------
|
||
hl.monitor({ output = "", mode = "preferred", position = "auto", scale = "auto" })
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Core settings.
|
||
-- ---------------------------------------------------------------------------
|
||
hl.config({
|
||
general = {
|
||
gaps_in = 5,
|
||
gaps_out = 10,
|
||
border_size = 2,
|
||
col = {
|
||
active_border = "rgba(88c0d0ff)",
|
||
inactive_border = "rgba(4c566aff)",
|
||
},
|
||
layout = "dwindle",
|
||
resize_on_border = true,
|
||
},
|
||
decoration = {
|
||
rounding = 8,
|
||
active_opacity = 1.0,
|
||
inactive_opacity = 1.0,
|
||
blur = {
|
||
enabled = true,
|
||
size = 6,
|
||
passes = 2,
|
||
new_optimizations = true,
|
||
},
|
||
shadow = {
|
||
enabled = true,
|
||
range = 12,
|
||
render_power = 3,
|
||
},
|
||
},
|
||
input = {
|
||
kb_layout = "us",
|
||
follow_mouse = 1,
|
||
touchpad = { natural_scroll = true },
|
||
},
|
||
dwindle = {
|
||
preserve_split = true,
|
||
},
|
||
animations = {
|
||
enabled = true,
|
||
},
|
||
misc = {
|
||
disable_hyprland_logo = true,
|
||
disable_splash_rendering = true,
|
||
},
|
||
})
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Animations — snappy curves + per-leaf speeds (matches the reference laptop;
|
||
-- the hl.config default above is much slower).
|
||
-- ---------------------------------------------------------------------------
|
||
local curves = {
|
||
easeOutQuint = { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } },
|
||
easeInOutCubic = { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } },
|
||
almostLinear = { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } },
|
||
quick = { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } },
|
||
}
|
||
for name, curve in pairs(curves) do
|
||
hl.curve(name, curve)
|
||
end
|
||
|
||
local animations = {
|
||
{ leaf = "global", enabled = true, speed = 10, bezier = "default" },
|
||
{ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" },
|
||
{ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" },
|
||
{ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" },
|
||
{ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" },
|
||
{ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" },
|
||
{ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" },
|
||
{ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" },
|
||
}
|
||
for _, animation in ipairs(animations) do
|
||
hl.animation(animation)
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Window rules — float + centre the onboarding popups (kitty --class …).
|
||
-- ---------------------------------------------------------------------------
|
||
hl.window_rule({ name = "bos-keybinds", match = { class = "^(bos-keybinds)$" }, float = true, size = { 760, 720 } })
|
||
hl.window_rule({ name = "bos-welcome", match = { class = "^(bos-welcome)$" }, float = true, size = { 700, 560 } })
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Environment (vendor-neutral; no GPU-specific vars so it works on Intel/AMD).
|
||
-- ---------------------------------------------------------------------------
|
||
hl.env("XCURSOR_SIZE", "24")
|
||
hl.env("HYPRCURSOR_SIZE", "24")
|
||
hl.env("XCURSOR_THEME", "Bibata-Modern-Ice")
|
||
hl.env("MOZ_ENABLE_WAYLAND", "1")
|
||
hl.env("QT_QPA_PLATFORM", "wayland;xcb")
|
||
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct")
|
||
hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
|
||
hl.env("SDL_VIDEODRIVER", "wayland")
|
||
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "auto")
|
||
hl.env("_JAVA_AWT_WM_NONREPARENTING", "1")
|
||
|
||
-- kitty sets its own background_opacity (see kitty.conf), so the global blur
|
||
-- above blurs behind the terminal while keeping text fully opaque.
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Standard BOS keybinds (SUPER = mod).
|
||
-- ---------------------------------------------------------------------------
|
||
-- Apps / window management
|
||
hl.bind(mod .. " + RETURN", hl.dsp.exec_cmd("kitty"))
|
||
hl.bind(mod .. " + BACKSPACE", hl.dsp.window.close())
|
||
hl.bind(mod .. " + SPACE", hl.dsp.exec_cmd("breadbox"))
|
||
hl.bind(mod .. " + E", hl.dsp.exec_cmd("nautilus"))
|
||
hl.bind(mod .. " + B", hl.dsp.exec_cmd("zen-browser"))
|
||
hl.bind(mod .. " + U", hl.dsp.exec_cmd("breadpad"))
|
||
hl.bind(mod .. " + M", hl.dsp.exec_cmd("breadman"))
|
||
hl.bind(mod .. " + comma", hl.dsp.exec_cmd("bos-settings"))
|
||
hl.bind(mod .. " + slash", hl.dsp.exec_cmd("bos-keybinds"))
|
||
hl.bind(mod .. " + L", hl.dsp.exec_cmd("loginctl lock-session"))
|
||
hl.bind(mod .. " + F", hl.dsp.window.fullscreen({ action = "toggle" }))
|
||
hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" }))
|
||
hl.bind(mod .. " + SHIFT + V", hl.dsp.exec_cmd([[bash -c 'cliphist list | fzf --reverse --prompt="Clipboard > " | cliphist decode | wl-copy']]))
|
||
hl.bind(mod .. " + T", hl.dsp.layout("togglesplit"))
|
||
hl.bind(mod .. " + Tab", hl.dsp.focus({ urgent_or_last = true }))
|
||
hl.bind(mod .. " + N", hl.dsp.exit())
|
||
|
||
-- Screenshots (grim + slurp + wl-clipboard)
|
||
hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd([[bash -c 'mkdir -p ~/Pictures/Screenshots && grim -g "$(slurp)" ~/Pictures/Screenshots/$(date +%Y%m%d-%H%M%S).png']]))
|
||
hl.bind(mod .. " + SHIFT + C", hl.dsp.exec_cmd([[bash -c 'grim -g "$(slurp)" - | wl-copy']]))
|
||
hl.bind(mod .. " + SHIFT + P", hl.dsp.exec_cmd([[bash -c 'mkdir -p ~/Pictures/Screenshots && grim ~/Pictures/Screenshots/$(date +%Y%m%d-%H%M%S).png']]))
|
||
|
||
-- Focus (directional)
|
||
hl.bind(mod .. " + left", hl.dsp.focus({ direction = "left" }))
|
||
hl.bind(mod .. " + right", hl.dsp.focus({ direction = "right" }))
|
||
hl.bind(mod .. " + up", hl.dsp.focus({ direction = "up" }))
|
||
hl.bind(mod .. " + down", hl.dsp.focus({ direction = "down" }))
|
||
|
||
-- Move window (directional, vim keys)
|
||
hl.bind(mod .. " + SHIFT + h", hl.dsp.window.move({ direction = "left" }))
|
||
hl.bind(mod .. " + SHIFT + j", hl.dsp.window.move({ direction = "down" }))
|
||
hl.bind(mod .. " + SHIFT + k", hl.dsp.window.move({ direction = "up" }))
|
||
hl.bind(mod .. " + SHIFT + l", hl.dsp.window.move({ direction = "right" }))
|
||
|
||
-- Resize active window (arrows)
|
||
hl.bind(mod .. " + SHIFT + right", hl.dsp.window.resize({ x = 30, y = 0, relative = true }), { repeating = true })
|
||
hl.bind(mod .. " + SHIFT + left", hl.dsp.window.resize({ x = -30, y = 0, relative = true }), { repeating = true })
|
||
hl.bind(mod .. " + SHIFT + up", hl.dsp.window.resize({ x = 0, y = -30, relative = true }), { repeating = true })
|
||
hl.bind(mod .. " + SHIFT + down", hl.dsp.window.resize({ x = 0, y = 30, relative = true }), { repeating = true })
|
||
|
||
-- Workspaces 1–10 (0 = workspace 10)
|
||
for i = 1, 10 do
|
||
local key = tostring(i % 10)
|
||
hl.bind(mod .. " + " .. key, hl.dsp.focus({ workspace = i }))
|
||
hl.bind(mod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
|
||
end
|
||
|
||
-- Workspace cycling
|
||
hl.bind(mod .. " + bracketright", hl.dsp.focus({ workspace = "e+1" }))
|
||
hl.bind(mod .. " + bracketleft", hl.dsp.focus({ workspace = "e-1" }))
|
||
hl.bind(mod .. " + SHIFT + bracketright", hl.dsp.window.move({ workspace = "e+1" }))
|
||
hl.bind(mod .. " + SHIFT + bracketleft", hl.dsp.window.move({ workspace = "e-1" }))
|
||
|
||
-- Mouse
|
||
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 .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
||
hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
||
|
||
-- Media / hardware keys (work locked, i.e. on the lock screen too)
|
||
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @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("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%+"), { locked = true, repeating = true })
|
||
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%-"), { locked = true, repeating = 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("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Autostart. polkit agent + the bread ecosystem + idle daemon + wallpaper.
|
||
-- (bos-live-setup appends the live-installer launch below this on the ISO.)
|
||
-- ---------------------------------------------------------------------------
|
||
hl.on("hyprland.start", function()
|
||
local startup = {
|
||
-- Generate the shared bread GUI stylesheet first, so breadbar/breadbox/
|
||
-- bos-settings load it on start (they also live-reload if it changes).
|
||
"bread-theme generate",
|
||
-- Global dark theme: GTK4/libadwaita + GTK3 theme + icon + cursor.
|
||
"gsettings set org.gnome.desktop.interface color-scheme prefer-dark",
|
||
"gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark",
|
||
"gsettings set org.gnome.desktop.interface icon-theme Papirus-Dark",
|
||
"gsettings set org.gnome.desktop.interface cursor-theme Bibata-Modern-Ice",
|
||
"gsettings set org.gnome.desktop.interface cursor-size 24",
|
||
-- Clipboard history daemon (feeds SUPER+V history picker via wl-paste).
|
||
"wl-paste --type text --watch cliphist store",
|
||
"/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1",
|
||
"awww-daemon",
|
||
-- set the default wallpaper once the daemon is up (retry until ready)
|
||
[[bash -c 'until awww img /usr/share/backgrounds/bos/bread-background.png 2>/dev/null; do sleep 0.3; done']],
|
||
-- breadd runs as a systemd user service (~/.config/systemd/user/breadd.service,
|
||
-- enabled in skel). It autostarts at login but before Hyprland exists, so
|
||
-- push the compositor's Wayland env into the user manager and restart breadd
|
||
-- to pick it up — that's how it gets HYPRLAND_INSTANCE_SIGNATURE to talk to Hyprland.
|
||
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
|
||
"systemctl --user restart breadd",
|
||
"breadbar",
|
||
"breadbox-sync",
|
||
"hypridle",
|
||
-- first-boot onboarding (self-gates after the first run)
|
||
"bos-welcome",
|
||
}
|
||
for _, cmd in ipairs(startup) do
|
||
hl.dispatch(hl.dsp.exec_cmd(cmd))
|
||
end
|
||
end)
|