This commit is contained in:
+13
-17
@@ -26,24 +26,20 @@
|
|||||||
# RetroArch (bare — cores/ROMs added by hand, WebDAV configured in-app).
|
# RetroArch (bare — cores/ROMs added by hand, WebDAV configured in-app).
|
||||||
custom.gaming.retroarch = true;
|
custom.gaming.retroarch = true;
|
||||||
|
|
||||||
wayland.windowManager.hyprland.settings = {
|
wayland.windowManager.hyprland.extraConfig = ''
|
||||||
exec-once = [
|
hl.workspace_rule({ workspace = "1", monitor = "DP-3" })
|
||||||
];
|
hl.workspace_rule({ workspace = "2", monitor = "eDP-1" })
|
||||||
|
hl.workspace_rule({ workspace = "3", monitor = "eDP-1" })
|
||||||
|
hl.workspace_rule({ workspace = "4", monitor = "eDP-1" })
|
||||||
|
|
||||||
workspace = [
|
hl.window_rule({
|
||||||
"1, monitor:DP-3"
|
match = { class = "bevy-.*" },
|
||||||
"2, monitor:eDP-1"
|
no_initial_focus = true,
|
||||||
"3, monitor:eDP-1"
|
float = true,
|
||||||
"4, monitor:eDP-1"
|
size = "800 600",
|
||||||
];
|
move = "100%-800 100%-600",
|
||||||
|
})
|
||||||
windowrule = [
|
'';
|
||||||
"no_initial_focus on, match:class bevy-.*"
|
|
||||||
"float on, match:class bevy-.*"
|
|
||||||
"size 800 600, match:class bevy-.*"
|
|
||||||
"move 100%-800 100%-600, match:class bevy-.*"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# v4 `bar.density` has no v5 equivalent; spacing is tuned via
|
# v4 `bar.density` has no v5 equivalent; spacing is tuned via
|
||||||
# bar.main.padding / widget_spacing / thickness if needed.
|
# bar.main.padding / widget_spacing / thickness if needed.
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
_:
|
_:
|
||||||
{
|
{
|
||||||
wayland.windowManager.hyprland.settings = {
|
# Hyprland 0.55+ is Lua-only (see home/common/programs/hyprland).
|
||||||
monitor = [
|
wayland.windowManager.hyprland.extraConfig = ''
|
||||||
"eDP-1, preferred, 0x0, 1, bitdepth, 8"
|
hl.monitor({ output = "eDP-1", mode = "preferred", position = "0x0", scale = 1, bitdepth = 8 })
|
||||||
"DP-1, preferred, auto-up, 1"
|
hl.monitor({ output = "DP-1", mode = "preferred", position = "auto-up", scale = 1 })
|
||||||
"DP-2, preferred, auto-up, 1"
|
hl.monitor({ output = "DP-2", mode = "preferred", position = "auto-up", scale = 1 })
|
||||||
"DP-3, preferred, auto-up, 1"
|
hl.monitor({ output = "DP-3", mode = "preferred", position = "auto-up", scale = 1 })
|
||||||
"DP-4, preferred, auto-up, 1"
|
hl.monitor({ output = "DP-4", mode = "preferred", position = "auto-up", scale = 1 })
|
||||||
];
|
'';
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,260 +66,214 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
package = null;
|
package = null;
|
||||||
portalPackage = null;
|
portalPackage = null;
|
||||||
# Keep the hyprlang config format (the new default is "lua"); our
|
# Hyprland 0.55+ removed the hyprlang parser entirely — the config format
|
||||||
# settings are written as hyprlang.
|
# is now Lua only (see home/common/programs/hyprland — the whole config
|
||||||
configType = "hyprlang";
|
# below is raw Lua using the hl.* API). home-manager still emits its own
|
||||||
|
# systemd-session start hook, so we don't duplicate that here.
|
||||||
|
configType = "lua";
|
||||||
plugins = [ ];
|
plugins = [ ];
|
||||||
settings = {
|
extraConfig = ''
|
||||||
plugin = {
|
local mod = "SUPER"
|
||||||
overview = {
|
|
||||||
autoDrag = true;
|
|
||||||
exitOnClick = true;
|
|
||||||
exitOnSwitch = true;
|
|
||||||
showNewWorkspace = false;
|
|
||||||
showEmptyWorkspace = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
input = {
|
|
||||||
kb_options = "ctrl:nocaps";
|
|
||||||
repeat_delay = 150;
|
|
||||||
|
|
||||||
touchpad = {
|
-- look & feel / behaviour
|
||||||
natural_scroll = "yes";
|
hl.config({
|
||||||
};
|
general = {
|
||||||
numlock_by_default = true;
|
border_size = 1,
|
||||||
};
|
gaps_in = 0,
|
||||||
|
gaps_out = 0,
|
||||||
|
snap = {
|
||||||
|
enabled = true,
|
||||||
|
border_overlap = true,
|
||||||
|
},
|
||||||
|
col = {
|
||||||
|
inactive_border = "0x99999999",
|
||||||
|
active_border = "0x99999999",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
cursor = {
|
input = {
|
||||||
no_hardware_cursors = "yes";
|
kb_options = "ctrl:nocaps",
|
||||||
# Auto-hide the pointer after 3s of no movement so a parked cursor
|
repeat_delay = 150,
|
||||||
# doesn't sit on top of a game (visible in the stream too). It
|
touchpad = {
|
||||||
# reappears the instant the mouse moves.
|
natural_scroll = true,
|
||||||
inactive_timeout = 3;
|
},
|
||||||
};
|
numlock_by_default = true,
|
||||||
|
},
|
||||||
|
|
||||||
general = {
|
cursor = {
|
||||||
border_size = 1;
|
no_hardware_cursors = true,
|
||||||
gaps_in = 0;
|
-- Auto-hide the pointer after 3s of no movement so a parked cursor
|
||||||
gaps_out = 0;
|
-- doesn't sit on top of a game (visible in the stream too). It
|
||||||
snap = {
|
-- reappears the instant the mouse moves.
|
||||||
enabled = true;
|
inactive_timeout = 3,
|
||||||
border_overlap = true;
|
},
|
||||||
};
|
|
||||||
|
|
||||||
"col.inactive_border" = "0x99999999";
|
misc = {
|
||||||
"col.active_border" = "0x99999999";
|
disable_hyprland_logo = true,
|
||||||
};
|
disable_splash_rendering = true,
|
||||||
|
on_focus_under_fullscreen = 2,
|
||||||
|
},
|
||||||
|
|
||||||
misc = {
|
ecosystem = {
|
||||||
disable_hyprland_logo = true;
|
no_update_news = true,
|
||||||
disable_splash_rendering = true;
|
},
|
||||||
on_focus_under_fullscreen = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
ecosystem = {
|
xwayland = {
|
||||||
no_update_news = true;
|
force_zero_scaling = true,
|
||||||
};
|
},
|
||||||
|
|
||||||
xwayland = {
|
decoration = {
|
||||||
force_zero_scaling = true;
|
blur = {
|
||||||
};
|
enabled = true,
|
||||||
|
popups = false,
|
||||||
|
},
|
||||||
|
shadow = {
|
||||||
|
enabled = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
decoration = {
|
-- keep default animations, but speed them all up
|
||||||
blur = {
|
hl.animation({ leaf = "global", enabled = true, speed = 2, bezier = "default" })
|
||||||
enabled = true;
|
|
||||||
popups = false;
|
|
||||||
};
|
|
||||||
shadow = {
|
|
||||||
enabled = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# keep default animations, but speed them all up
|
-- environment
|
||||||
animations = {
|
hl.env("GDK_SCALE", "2.0")
|
||||||
animation = [
|
hl.env("XCURSOR_SIZE", "${toString cfg.cursorSize}")
|
||||||
"global, 1, 2, default"
|
hl.env("HYPRCURSOR_THEME", "rose-pine-hyprcursor")
|
||||||
];
|
hl.env("HYPRCURSOR_SIZE", "${toString cfg.cursorSize}")
|
||||||
};
|
-- Route Qt6 apps (incl. the xdph screen-share picker) through qt6ct so
|
||||||
|
-- the noctalia-generated color scheme actually applies. Without this
|
||||||
|
-- var the qt6ct.conf is never read.
|
||||||
|
hl.env("QT_QPA_PLATFORMTHEME", "qt6ct")
|
||||||
|
|
||||||
env = [
|
-- autostart
|
||||||
"GDK_SCALE, 2.0"
|
hl.on("hyprland.start", function()
|
||||||
"XCURSOR_SIZE, ${toString cfg.cursorSize}"
|
-- Import the Qt theme var too, so the dbus/systemd-activated
|
||||||
"HYPRCURSOR_THEME, rose-pine-hyprcursor"
|
-- xdg-desktop-portal-hyprland (and its share-picker) inherit it.
|
||||||
"HYPRCURSOR_SIZE, ${toString cfg.cursorSize}"
|
hl.exec_cmd("dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME")
|
||||||
# Route Qt6 apps (incl. the xdph screen-share picker) through qt6ct so
|
hl.exec_cmd("hyprctl setcursor Nordzy-cursors ${toString cfg.cursorSize}")
|
||||||
# the noctalia-generated color scheme actually applies. Without this
|
hl.exec_cmd("hyprsunset")
|
||||||
# var the qt6ct.conf is never read.
|
hl.exec_cmd("noctalia")
|
||||||
"QT_QPA_PLATFORMTHEME, qt6ct"
|
hl.exec_cmd("yogurt --tray")
|
||||||
];
|
end)
|
||||||
|
|
||||||
"exec-once" = [
|
-- layer rules
|
||||||
# Import the Qt theme var too, so the dbus/systemd-activated
|
hl.layer_rule({ match = { namespace = "noctalia-wallpaper" }, blur = true, ignore_alpha = 0.5 })
|
||||||
# xdg-desktop-portal-hyprland (and its share-picker) inherit it.
|
|
||||||
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME"
|
|
||||||
"hyprctl setcursor Nordzy-cursors ${toString cfg.cursorSize}"
|
|
||||||
"hyprsunset"
|
|
||||||
"noctalia"
|
|
||||||
"yogurt --tray"
|
|
||||||
];
|
|
||||||
|
|
||||||
layerrule = [
|
-- workspace rules
|
||||||
"blur 1, match:namespace noctalia-wallpaper"
|
hl.workspace_rule({ workspace = "w[t1]", gaps_out = 0, gaps_in = 0 }) -- no gaps when only window
|
||||||
"ignore_alpha 0.5, match:namespace noctalia-wallpaper"
|
hl.workspace_rule({ workspace = "w[tg1]", gaps_out = 0, gaps_in = 0 })
|
||||||
];
|
hl.workspace_rule({ workspace = "f[1]", gaps_out = 0, gaps_in = 0 })
|
||||||
workspace = [
|
hl.workspace_rule({ workspace = "special:terminal", on_created_empty = "[float; size 1400 1000; center 1] kitty", persistent = false })
|
||||||
# no gaps when only window
|
hl.workspace_rule({ workspace = "special:yazi", on_created_empty = "[float; size ${cfg.yaziSize}; center 1] kitty --session sessions/yazi", persistent = false })
|
||||||
"w[t1], gapsout:0, gapsin:0"
|
|
||||||
"w[tg1], gapsout:0, gapsin:0"
|
|
||||||
"f[1], gapsout:0, gapsin:0"
|
|
||||||
|
|
||||||
"special:terminal, on-created-empty:[float; size 1400 1000; center 1] kitty, persistent:false"
|
-- window rules
|
||||||
"special:yazi, on-created-empty:[float; size ${cfg.yaziSize}; center 1] kitty --session sessions/yazi, persistent:false"
|
hl.window_rule({ match = { class = "com.gabm.satty" }, float = true })
|
||||||
];
|
|
||||||
|
|
||||||
windowrule = [
|
hl.window_rule({ match = { class = "imv" }, float = true, size = "1920 1080", move = "(cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5))" })
|
||||||
# satty
|
hl.window_rule({ match = { class = "mpv" }, float = true, size = "1920 1080", move = "(cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5))" })
|
||||||
"float on, match:class com.gabm.satty"
|
|
||||||
|
|
||||||
# imv
|
-- webcam overlay (`webcam` command) — small, pinned, bottom-right.
|
||||||
"float on, match:class imv"
|
-- Declared after the mpv class rule so its title match wins.
|
||||||
"size 1920 1080, match:class imv"
|
hl.window_rule({
|
||||||
"move (cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5)), match:class imv"
|
match = { title = "webcam-overlay" },
|
||||||
|
float = true,
|
||||||
|
size = "360 240",
|
||||||
|
move = "100%-380 100%-260",
|
||||||
|
pin = true,
|
||||||
|
no_initial_focus = true,
|
||||||
|
border_size = 0,
|
||||||
|
})
|
||||||
|
|
||||||
# mpv
|
hl.window_rule({ match = { class = "thunar" }, float = true, size = "1800 1200", move = "(cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5))" })
|
||||||
"float on, match:class mpv"
|
hl.window_rule({ match = { class = [[\.blueman-manager-wrapped]] }, float = true, size = "1200 800", move = "(cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5))" })
|
||||||
"size 1920 1080, match:class mpv"
|
hl.window_rule({ match = { class = [[org\.pulseaudio\.pavucontrol]] }, float = true, size = "1200 1200", move = "(cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5))" })
|
||||||
"move (cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5)), match:class mpv"
|
|
||||||
|
|
||||||
# webcam overlay (`webcam` command) — small, pinned, bottom-right.
|
-- wine / game installers (Inno Setup temp windows, e.g. Heroic/GOG).
|
||||||
# Title rules come after the mpv class rules so they win.
|
-- Their class is the random "setup_*.tmp" filename, so match by suffix.
|
||||||
"float on, match:title webcam-overlay"
|
hl.window_rule({ match = { class = [[.*\.tmp]] }, float = true, center = true })
|
||||||
"size 360 240, match:title webcam-overlay"
|
|
||||||
"move 100%-380 100%-260, match:title webcam-overlay"
|
|
||||||
"pin on, match:title webcam-overlay"
|
|
||||||
"no_initial_focus on, match:title webcam-overlay"
|
|
||||||
"border_size 0, match:title webcam-overlay"
|
|
||||||
|
|
||||||
# thunar
|
-- metamask
|
||||||
"float on, match:class thunar"
|
hl.window_rule({ match = { class = [[chrome-nkbihfbeogaeaoehlefnkodbefgpgknn-.*]] }, float = true })
|
||||||
"size 1800 1200, match:class thunar"
|
-- bitwarden, chrome
|
||||||
"move (cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5)), match:class thunar"
|
hl.window_rule({ match = { class = [[chrome-nngceckbapebfimnlniiiahkandclblb-.*]] }, float = true })
|
||||||
|
-- claude for chrome
|
||||||
|
hl.window_rule({ match = { class = [[chrome-fcoeoabgfenejglbffodgkkbkcdhcgfn-.*]] }, float = true, move = "(cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5))" })
|
||||||
|
|
||||||
# bluetooth
|
-- no gaps when only window
|
||||||
"float on, match:class \\.blueman-manager-wrapped"
|
hl.window_rule({ match = { float = false, workspace = "w[t1]" }, border_size = 0, rounding = 0 })
|
||||||
"size 1200 800, match:class \\.blueman-manager-wrapped"
|
hl.window_rule({ match = { float = false, workspace = "w[tg1]" }, border_size = 0, rounding = 0 })
|
||||||
"move (cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5)), match:class \\.blueman-manager-wrapped"
|
hl.window_rule({ match = { float = false, workspace = "f[1]" }, border_size = 0, rounding = 0 })
|
||||||
|
|
||||||
# pavucontrol
|
hl.window_rule({ match = { workspace = "special:terminal" }, center = true })
|
||||||
"float on, match:class org\\.pulseaudio\\.pavucontrol"
|
hl.window_rule({ match = { workspace = "special:yazi" }, center = true })
|
||||||
"size 1200 1200, match:class org\\.pulseaudio\\.pavucontrol"
|
|
||||||
"move (cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5)), match:class org\\.pulseaudio\\.pavucontrol"
|
|
||||||
|
|
||||||
# wine / game installers (Inno Setup temp windows, e.g. Heroic/GOG).
|
-- keybinds
|
||||||
# Their class is the random "setup_*.tmp" filename, so match by suffix.
|
hl.bind(mod .. " + N", hl.dsp.exec_cmd("noctalia msg panel-toggle control-center"))
|
||||||
"float on, match:class .*\\.tmp"
|
hl.bind(mod .. " + T", hl.dsp.exec_cmd("kitty"))
|
||||||
"center 1, match:class .*\\.tmp"
|
hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" }))
|
||||||
|
hl.bind(mod .. " + Q", hl.dsp.window.close())
|
||||||
|
hl.bind(mod .. " + F", hl.dsp.window.fullscreen({ mode = "fullscreen" }))
|
||||||
|
hl.bind(mod .. " + SHIFT + F", hl.dsp.window.fullscreen({ mode = "maximized" }))
|
||||||
|
|
||||||
# metamask
|
-- lock (routes through logind -> hypridle lock_cmd -> hyprlock)
|
||||||
"float on, match:class chrome-nkbihfbeogaeaoehlefnkodbefgpgknn-.*"
|
hl.bind(mod .. " + CTRL + L", hl.dsp.exec_cmd("loginctl lock-session"))
|
||||||
|
|
||||||
# bitwarden, chrome
|
hl.bind(mod .. " + space", hl.dsp.exec_cmd("noctalia msg panel-toggle launcher"))
|
||||||
"float on, match:class chrome-nngceckbapebfimnlniiiahkandclblb-.*"
|
|
||||||
|
|
||||||
# claude for chrome
|
-- printscreen
|
||||||
"float on, match:class chrome-fcoeoabgfenejglbffodgkkbkcdhcgfn-.*"
|
hl.bind("Print", hl.dsp.exec_cmd("hyprshot -m region --raw | satty --filename - --output-filename ~/downloads/screenshots/$(date +%Y-%m-%d_%H-%M-%S).png"))
|
||||||
"move (cursor_x-(window_w*0.5)) (cursor_y-(window_h*0.5)), match:class chrome-fcoeoabgfenejglbffodgkkbkcdhcgfn-.*"
|
hl.bind("SHIFT + Print", hl.dsp.exec_cmd("hyprshot -m window --raw | satty --filename - --output-filename ~/downloads/screenshots/$(date +%Y-%m-%d_%H-%M-%S).png"))
|
||||||
|
|
||||||
# no gaps when only window
|
-- move focus with mod + hjkl
|
||||||
"border_size 0, match:float 0, match:workspace w[t1]"
|
hl.bind(mod .. " + H", hl.dsp.focus({ direction = "left" }))
|
||||||
"rounding 0, match:float 0, match:workspace w[t1]"
|
hl.bind(mod .. " + J", hl.dsp.focus({ direction = "down" }))
|
||||||
"border_size 0, match:float 0, match:workspace w[tg1]"
|
hl.bind(mod .. " + K", hl.dsp.focus({ direction = "up" }))
|
||||||
"rounding 0, match:float 0, match:workspace w[tg1]"
|
hl.bind(mod .. " + L", hl.dsp.focus({ direction = "right" }))
|
||||||
"border_size 0, match:float 0, match:workspace f[1]"
|
|
||||||
"rounding 0, match:float 0, match:workspace f[1]"
|
|
||||||
|
|
||||||
"center 1, match:workspace special:terminal"
|
-- switch / move-to workspaces 1-6
|
||||||
"center 1, match:workspace special:yazi"
|
for i = 1, 6 do
|
||||||
];
|
hl.bind(mod .. " + " .. i, hl.dsp.focus({ workspace = i }))
|
||||||
|
hl.bind(mod .. " + SHIFT + " .. i, hl.dsp.window.move({ workspace = i }))
|
||||||
|
end
|
||||||
|
|
||||||
"$mod" = "SUPER";
|
-- move active window inside workspace
|
||||||
|
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" }))
|
||||||
|
|
||||||
bind = [
|
-- scroll through workspaces with mod + scroll
|
||||||
"$mod, n, exec, noctalia msg panel-toggle control-center"
|
hl.bind(mod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
|
||||||
"$mod, t, exec, kitty"
|
hl.bind(mod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
|
||||||
"$mod, v, togglefloating"
|
|
||||||
"$mod, q, killactive"
|
|
||||||
"$mod, f, fullscreen, 0"
|
|
||||||
"$mod SHIFT, f, fullscreen, 1"
|
|
||||||
|
|
||||||
# lock (routes through logind -> hypridle lock_cmd -> hyprlock)
|
-- special workspaces (toggle + recenter in one handler)
|
||||||
"$mod CTRL, l, exec, loginctl lock-session"
|
hl.bind(mod .. " + X", function()
|
||||||
|
hl.dispatch(hl.dsp.workspace.toggle_special("terminal"))
|
||||||
|
hl.dispatch(hl.dsp.window.center())
|
||||||
|
end)
|
||||||
|
hl.bind(mod .. " + E", function()
|
||||||
|
hl.dispatch(hl.dsp.workspace.toggle_special("yazi"))
|
||||||
|
hl.dispatch(hl.dsp.window.center())
|
||||||
|
end)
|
||||||
|
|
||||||
"$mod, space, exec, noctalia msg panel-toggle launcher"
|
-- volume / brightness (locked so they work on the lock screen; repeat on hold)
|
||||||
|
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @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("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl set 5%+"), { locked = true, repeating = true })
|
||||||
|
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl set 5%-"), { locked = true, repeating = true })
|
||||||
|
|
||||||
# printscreen
|
-- media keys (locked)
|
||||||
", Print, exec, hyprshot -m region --raw | satty --filename - --output-filename ~/downloads/screenshots/$(date +%Y-%m-%d_%H-%M-%S).png"
|
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("pactl set-sink-mute @DEFAULT_SINK@ toggle"), { locked = true })
|
||||||
"SHIFT, Print, exec, hyprshot -m window --raw | satty --filename - --output-filename ~/downloads/screenshots/$(date +%Y-%m-%d_%H-%M-%S).png"
|
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("volumectl -m toggle-mute"), { 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("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
|
||||||
|
|
||||||
# move focus with mod + arrows
|
-- move/resize windows with mod + LMB/RMB drag
|
||||||
"$mod, h, movefocus, l"
|
hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
||||||
"$mod, j, movefocus, d"
|
hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
||||||
"$mod, k, movefocus, u"
|
'';
|
||||||
"$mod, l, movefocus, r"
|
|
||||||
|
|
||||||
# Switch workspaces with mod + [0-9]
|
|
||||||
"$mod, 1, workspace, 1"
|
|
||||||
"$mod, 2, workspace, 2"
|
|
||||||
"$mod, 3, workspace, 3"
|
|
||||||
"$mod, 4, workspace, 4"
|
|
||||||
"$mod, 5, workspace, 5"
|
|
||||||
"$mod, 6, workspace, 6"
|
|
||||||
|
|
||||||
# Move active window to a workspace with mod + SHIFT + [0-9]
|
|
||||||
"$mod SHIFT, 1, movetoworkspace, 1"
|
|
||||||
"$mod SHIFT, 2, movetoworkspace, 2"
|
|
||||||
"$mod SHIFT, 3, movetoworkspace, 3"
|
|
||||||
"$mod SHIFT, 4, movetoworkspace, 4"
|
|
||||||
"$mod SHIFT, 5, movetoworkspace, 5"
|
|
||||||
"$mod SHIFT, 6, movetoworkspace, 6"
|
|
||||||
|
|
||||||
# Move active window inside workspace
|
|
||||||
"$mod SHIFT, h, movewindow, l"
|
|
||||||
"$mod SHIFT, j, movewindow, d"
|
|
||||||
"$mod SHIFT, k, movewindow, u"
|
|
||||||
"$mod SHIFT, l, movewindow, r"
|
|
||||||
|
|
||||||
# Scroll through existing workspaces with mod + scroll
|
|
||||||
"$mod, mouse_down, workspace, e+1"
|
|
||||||
"$mod, mouse_up, workspace, e-1"
|
|
||||||
|
|
||||||
# special workspaces
|
|
||||||
"$mod, x, togglespecialworkspace, terminal"
|
|
||||||
"$mod, x, centerwindow"
|
|
||||||
"$mod, e, togglespecialworkspace, yazi"
|
|
||||||
"$mod, e, centerwindow"
|
|
||||||
];
|
|
||||||
|
|
||||||
bindel = [
|
|
||||||
", XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
|
|
||||||
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
|
|
||||||
", XF86MonBrightnessUp, exec, brightnessctl set 5%+"
|
|
||||||
", XF86MonBrightnessDown, exec, brightnessctl set 5%-"
|
|
||||||
];
|
|
||||||
bindl = [
|
|
||||||
", XF86AudioMute, exec, pactl set-sink-mute @DEFAULT_SINK@ toggle"
|
|
||||||
", XF86AudioMicMute, exec, volumectl -m toggle-mute"
|
|
||||||
", XF86AudioNext, exec, playerctl next"
|
|
||||||
", XF86AudioPrev, exec, playerctl previous"
|
|
||||||
", XF86AudioPlay, exec, playerctl play-pause"
|
|
||||||
];
|
|
||||||
|
|
||||||
bindm = [
|
|
||||||
# Move/resize windows with mod + LMB/RMB and dragging
|
|
||||||
"$mod, mouse:272, movewindow"
|
|
||||||
"$mod, mouse:273, resizewindow"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services.hyprpaper.enable = false;
|
services.hyprpaper.enable = false;
|
||||||
|
|||||||
@@ -90,19 +90,15 @@ in
|
|||||||
home.file.".face".source = avatar;
|
home.file.".face".source = avatar;
|
||||||
|
|
||||||
# force overwrite files that noctalia may have turned into regular files
|
# force overwrite files that noctalia may have turned into regular files
|
||||||
xdg.configFile."hypr/hyprland.conf".force = true;
|
# (Hyprland 0.55+ is Lua-only, so the managed file is now hyprland.lua)
|
||||||
|
xdg.configFile."hypr/hyprland.lua".force = true;
|
||||||
xdg.configFile."gtk-3.0/gtk.css".force = true;
|
xdg.configFile."gtk-3.0/gtk.css".force = true;
|
||||||
|
|
||||||
# source noctalia-generated color theme in hyprland (v5 path).
|
# NOTE: noctalia still writes its color theme to ~/.config/hypr/noctalia.conf
|
||||||
# Hyprland globs every source path, so a missing file errors as
|
# in *hyprlang* syntax ($primary = rgb(...), general { col.active_border = ... }).
|
||||||
# "globbing error: found no match" — pre-create an empty placeholder
|
# Hyprland 0.55+ dropped the hyprlang parser entirely, so that file can no
|
||||||
# so the first boot doesn't fail before noctalia has run.
|
# longer be `source`d — there is no hl.source, and the lua config can't parse
|
||||||
home.activation.ensureNoctaliaHyprConf = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
# hyprlang. Hyprland therefore uses the static border colors from
|
||||||
run mkdir -p "$HOME/.config/hypr"
|
# ../default.nix instead of noctalia's themed ones. To restore live theming,
|
||||||
run touch "$HOME/.config/hypr/noctalia.conf"
|
# noctalia would need to emit a Lua snippet we can require() here.
|
||||||
'';
|
|
||||||
|
|
||||||
wayland.windowManager.hyprland.settings.source = [
|
|
||||||
"~/.config/hypr/noctalia.conf"
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-68
@@ -38,84 +38,74 @@
|
|||||||
# 4K@1x monitors render the Electron AI apps tiny; scale their UI up. Tune to taste.
|
# 4K@1x monitors render the Electron AI apps tiny; scale their UI up. Tune to taste.
|
||||||
custom.aiApps.deviceScaleFactor = "1.5";
|
custom.aiApps.deviceScaleFactor = "1.5";
|
||||||
|
|
||||||
wayland.windowManager.hyprland.settings = {
|
wayland.windowManager.hyprland.extraConfig = ''
|
||||||
exec-once = [
|
hl.on("hyprland.start", function()
|
||||||
# boot-into-lock: paired with SDDM autologin (host config), lock the
|
-- boot-into-lock: paired with SDDM autologin (host config), lock the
|
||||||
# session the moment Hyprland starts so a cold boot lands on hyprlock, not
|
-- session the moment Hyprland starts so a cold boot lands on hyprlock, not
|
||||||
# an open desktop. A brief flash before it paints is possible.
|
-- an open desktop. A brief flash before it paints is possible.
|
||||||
"hyprlock"
|
hl.exec_cmd("hyprlock")
|
||||||
"$HOME/.local/bin/hyprpaper-rotate"
|
hl.exec_cmd("$HOME/.local/bin/hyprpaper-rotate")
|
||||||
];
|
end)
|
||||||
|
|
||||||
windowrule = [
|
-- ethui-dev
|
||||||
# ethui-dev
|
hl.window_rule({ match = { title = "ethui-dev.*" }, workspace = "2", no_initial_focus = true })
|
||||||
"workspace 2, match:title ethui-dev.*"
|
hl.window_rule({ match = { title = "ethui-dev - dialog.*" }, float = true })
|
||||||
"no_initial_focus on, match:title ethui-dev.*"
|
|
||||||
"float on, match:title ethui-dev - dialog.*"
|
|
||||||
|
|
||||||
"workspace 1, match:title ethui.*"
|
hl.window_rule({ match = { title = "ethui.*" }, workspace = "1", no_initial_focus = true })
|
||||||
"no_initial_focus on, match:title ethui.*"
|
hl.window_rule({ match = { title = "ethui - dialog.*" }, float = true })
|
||||||
"float on, match:title ethui - dialog.*"
|
|
||||||
|
|
||||||
"workspace 1, match:title ^\\[bevy\\].*"
|
hl.window_rule({ match = { title = [[^\[bevy\].*]] }, workspace = "1" })
|
||||||
|
|
||||||
"workspace 3 silent, match:class bevy-.*"
|
hl.window_rule({ match = { class = "bevy-.*" }, workspace = "3 silent", no_initial_focus = true, fullscreen = true })
|
||||||
"no_initial_focus on, match:class bevy-.*"
|
|
||||||
"fullscreen on, match:class bevy-.*"
|
|
||||||
|
|
||||||
"workspace 1 silent, match:title egui-.*"
|
hl.window_rule({
|
||||||
"border_size 0, match:title egui-.*"
|
match = { title = "egui-.*" },
|
||||||
"float on, match:title egui-.*"
|
workspace = "1 silent",
|
||||||
"no_blur on, match:title egui-.*"
|
border_size = 0,
|
||||||
"move 100%-w-20 100%-h-20, match:title egui-.*"
|
float = true,
|
||||||
|
no_blur = true,
|
||||||
|
move = "100%-w-20 100%-h-20",
|
||||||
|
})
|
||||||
|
|
||||||
# gaming: Steam/Proton titles run under XWayland with WM_CLASS
|
-- gaming: Steam/Proton titles run under XWayland with WM_CLASS
|
||||||
# steam_app_<appid>. Send them fullscreen to workspace 3 (DP-1) — the same
|
-- steam_app_<appid>. Send them fullscreen to workspace 3 (DP-1) — the same
|
||||||
# streamed workspace as Big Picture below — instead of opening tiled on
|
-- streamed workspace as Big Picture below — instead of opening tiled on
|
||||||
# whatever monitor Steam is on.
|
-- whatever monitor Steam is on.
|
||||||
# Native (non-Proton) games have their own class — grab it with
|
-- Native (non-Proton) games have their own class — grab it with
|
||||||
# `hyprctl clients | grep -iE "class|title"` while the game runs and add a
|
-- `hyprctl clients | grep -iE "class|title"` while the game runs and add a
|
||||||
# matching line here.
|
-- matching rule here.
|
||||||
"workspace 3, match:class steam_app_.*"
|
hl.window_rule({ match = { class = "steam_app_.*" }, workspace = "3", fullscreen = true })
|
||||||
"fullscreen on, match:class steam_app_.*"
|
|
||||||
|
|
||||||
# Steam Big Picture shares the plain `steam` WM_CLASS with the normal
|
-- Steam Big Picture shares the plain `steam` WM_CLASS with the normal
|
||||||
# client, so match its title instead. Without this it opens on whatever
|
-- client, so match its title instead. Without this it opens on whatever
|
||||||
# monitor the cursor is on, not the streamed one. Pin it to ws3 — DP-1's
|
-- monitor the cursor is on, not the streamed one. Pin it to ws3 — DP-1's
|
||||||
# normal persistent workspace, which is what the stream captures. `.`
|
-- normal persistent workspace, which is what the stream captures. `.`
|
||||||
# stands in for the literal spaces to keep the rule string unambiguous.
|
-- stands in for the literal spaces to keep the rule string unambiguous.
|
||||||
"workspace 3, match:title Steam.Big.Picture.Mode"
|
hl.window_rule({ match = { title = "Steam.Big.Picture.Mode" }, workspace = "3", fullscreen = true })
|
||||||
"fullscreen on, match:title Steam.Big.Picture.Mode"
|
|
||||||
|
|
||||||
# Heroic (GOG/Epic launcher) streamed via Moonlight — `heroic --console`.
|
-- Heroic (GOG/Epic launcher) streamed via Moonlight — `heroic --console`.
|
||||||
# Tile it on ws3 (DP-1, the captured screen) rather than letting it float
|
-- Tile it on ws3 (DP-1, the captured screen) rather than letting it float
|
||||||
# or land on whatever monitor the cursor is on. Games it launches are
|
-- or land on whatever monitor the cursor is on. Games it launches are
|
||||||
# Proton titles → caught by the steam_app_ rules above; native ones need
|
-- Proton titles → caught by the steam_app_ rules above; native ones need
|
||||||
# their own class added here.
|
-- their own class added here.
|
||||||
"workspace 3, match:class heroic"
|
hl.window_rule({ match = { class = "heroic" }, workspace = "3", tile = true })
|
||||||
"tile on, match:class heroic"
|
|
||||||
|
|
||||||
# RetroArch streamed via Moonlight (also registered as a Sunshine app).
|
-- RetroArch streamed via Moonlight (also registered as a Sunshine app).
|
||||||
# Native Wayland app_id is `com.libretro.RetroArch` (set at map time) — the
|
-- Native Wayland app_id is `com.libretro.RetroArch` (set at map time) — the
|
||||||
# exact string is needed so the rule applies on spawn; a loose `retroarch`
|
-- exact string is needed so the rule applies on spawn; a loose `retroarch`
|
||||||
# match misses at map and the window flashes onto the active workspace
|
-- match misses at map and the window flashes onto the active workspace
|
||||||
# first. `silent` sends it to ws3 (DP-1, the captured screen) without
|
-- first. `silent` sends it to ws3 (DP-1, the captured screen) without
|
||||||
# yanking focus off whatever workspace you're driving from.
|
-- yanking focus off whatever workspace you're driving from.
|
||||||
"workspace 3 silent, match:class com.libretro.RetroArch"
|
hl.window_rule({ match = { class = "com.libretro.RetroArch" }, workspace = "3 silent", fullscreen = true })
|
||||||
"fullscreen on, match:class com.libretro.RetroArch"
|
|
||||||
];
|
|
||||||
|
|
||||||
render = {
|
-- Experiment: was `true`. With a fullscreen game direct-scanning-out,
|
||||||
# Experiment: was `true`. With a fullscreen game direct-scanning-out,
|
-- Sunshine starting a capture forces the compositor off the direct path,
|
||||||
# Sunshine starting a capture forces the compositor off the direct path,
|
-- and that surface/context churn is a suspected trigger for Chromium's
|
||||||
# and that surface/context churn is a suspected trigger for Chromium's
|
-- `EGL_CONTEXT_LOST` crash (Big Picture drops to software rendering mid-
|
||||||
# `EGL_CONTEXT_LOST` crash (Big Picture drops to software rendering mid-
|
-- stream). Disabled to test whether the crashes stop. If it makes no
|
||||||
# stream). Disabled to test whether the crashes stop. If it makes no
|
-- difference, flip back to `true` for the local fullscreen latency win.
|
||||||
# difference, flip back to `true` for the local fullscreen latency win.
|
hl.config({ render = { direct_scanout = false } })
|
||||||
direct_scanout = false;
|
'';
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.noctalia.settings = {
|
programs.noctalia.settings = {
|
||||||
shell.ui_scale = 1.25;
|
shell.ui_scale = 1.25;
|
||||||
|
|||||||
+16
-17
@@ -1,20 +1,19 @@
|
|||||||
_:
|
_:
|
||||||
{
|
{
|
||||||
wayland.windowManager.hyprland.settings = {
|
# Hyprland 0.55+ is Lua-only (see home/common/programs/hyprland); monitors and
|
||||||
monitor = [
|
# workspace rules are expressed with the hl.* API in raw Lua.
|
||||||
# List HDMI-A-1 first so it becomes the primary monitor (ID 0)
|
wayland.windowManager.hyprland.extraConfig = ''
|
||||||
"HDMI-A-1, 3840x2160, 0x2160, 1"
|
-- monitors (HDMI-A-1 listed first so it becomes the primary, ID 0)
|
||||||
# Then the other monitors
|
hl.monitor({ output = "HDMI-A-1", mode = "3840x2160", position = "0x2160", scale = 1 })
|
||||||
"DP-2, 3840x2160, 3840x180, 1, transform, 1"
|
hl.monitor({ output = "DP-2", mode = "3840x2160", position = "3840x180", scale = 1, transform = 1 })
|
||||||
"DP-1, 3840x2160, 0x0, 1"
|
hl.monitor({ output = "DP-1", mode = "3840x2160", position = "0x0", scale = 1 })
|
||||||
];
|
|
||||||
workspace = [
|
-- workspace -> monitor bindings
|
||||||
"1, monitor:HDMI-A-1, default:true, persistent:true" # bottom-left (primary)
|
hl.workspace_rule({ workspace = "1", monitor = "HDMI-A-1", default = true, persistent = true }) -- bottom-left (primary)
|
||||||
"2, monitor:DP-2, persistent:true" # right (vertical)
|
hl.workspace_rule({ workspace = "2", monitor = "DP-2", persistent = true }) -- right (vertical)
|
||||||
"3, monitor:DP-1, persistent:true" # top-left
|
hl.workspace_rule({ workspace = "3", monitor = "DP-1", persistent = true }) -- top-left
|
||||||
"4, monitor:HDMI-A-1" # bottom-left
|
hl.workspace_rule({ workspace = "4", monitor = "HDMI-A-1" })
|
||||||
"5, monitor:DP-2" # right (vertical)
|
hl.workspace_rule({ workspace = "5", monitor = "DP-2" })
|
||||||
"6, monitor:DP-1" # top-left
|
hl.workspace_rule({ workspace = "6", monitor = "DP-1" })
|
||||||
];
|
'';
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
settings = {
|
settings = {
|
||||||
General = {
|
General = {
|
||||||
Enable = "Source,Sink,Media,Socket";
|
Enable = "Source,Sink,Media,Socket";
|
||||||
|
# Required for LE Audio (BAP): ISO sockets / CIS live behind
|
||||||
|
# BlueZ's experimental gate. Without this the XM6 falls back to
|
||||||
|
# classic A2DP, and mic use forces the HSP/HFP downgrade.
|
||||||
|
Experimental = true;
|
||||||
|
KernelExperimental = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,14 @@
|
|||||||
# GTK/GNOME services still useful on Wayland
|
# GTK/GNOME services still useful on Wayland
|
||||||
programs.dconf.enable = true;
|
programs.dconf.enable = true;
|
||||||
|
|
||||||
|
# Route the Secret portal (org.freedesktop.portal.Secret) to gnome-keyring.
|
||||||
|
# Hyprland's portal doesn't implement Secret, and the preferred backend list
|
||||||
|
# (hyprland;gtk) has no Secret provider — so without this, the portal exposes
|
||||||
|
# no Secret interface at all. Electron apps (e.g. Claude Desktop) fetch their
|
||||||
|
# token-encryption key through this portal; when it's missing, os_crypt init
|
||||||
|
# fails (prev_init_success:false) and they can't persist a login across boots.
|
||||||
|
xdg.portal.config.common."org.freedesktop.impl.portal.Secret" = "gnome-keyring";
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
# thumbnail support for images
|
# thumbnail support for images
|
||||||
tumbler.enable = true;
|
tumbler.enable = true;
|
||||||
|
|||||||
@@ -7,5 +7,30 @@
|
|||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
alsa.support32Bit = true;
|
alsa.support32Bit = true;
|
||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
|
|
||||||
|
# Disable the LC3-SWB HFP codec. BlueZ 5.86's experimental LC3 super-wideband
|
||||||
|
# headset path (lit up by hardware.bluetooth Experimental=true) is broken for
|
||||||
|
# the Sony WF-1000XM6: when a call opens the mic, WirePlumber auto-switches to
|
||||||
|
# the highest-priority headset profile (LC3-SWB), the audio link fails to set
|
||||||
|
# up ("Hands-Free Voice gateway: Transport endpoint is not connected"), and the
|
||||||
|
# buds reset with no call audio/mic. Omitting lc3_swb from the codec allow-list
|
||||||
|
# makes autoswitch fall back to mSBC, which is stable wideband voice. Everything
|
||||||
|
# else (A2DP LDAC/AAC/SBC, and lc3 for any future LE Audio) stays enabled.
|
||||||
|
wireplumber.extraConfig."51-disable-lc3swb-hfp" = {
|
||||||
|
"monitor.bluez.properties" = {
|
||||||
|
"bluez5.codecs" = [
|
||||||
|
"sbc"
|
||||||
|
"sbc_xq"
|
||||||
|
"aac"
|
||||||
|
"aptx"
|
||||||
|
"aptx_hd"
|
||||||
|
"aptx_ll"
|
||||||
|
"ldac"
|
||||||
|
"lc3"
|
||||||
|
"cvsd"
|
||||||
|
"msbc"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user