diff --git a/settings/hyprland.nix b/settings/hyprland.nix index 84cf839..76e97de 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -224,7 +224,9 @@ in # show as urgent in the bar instead. focus_on_activate = false; vrr = 2; - background_color = rgb c.base00; + # Black, not base00: login hands over greeter fade → black → + # Quickshell wallpaper fade, and only shows before that maps. + background_color = "rgb(000000)"; # If quickshell dies while holding the session lock, let its # restarted instance re-attach instead of a dead red screen. allow_session_lock_restore = true; diff --git a/settings/quickshell.nix b/settings/quickshell.nix index 2b69aa3..fc1300f 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -1137,6 +1137,15 @@ in id: wallLayer anchors.fill: parent + // Rise out of black at login: the greeter fades + // to black and Hyprland starts on black + // (misc.background_color), so no step shows. + NumberAnimation on opacity { + from: 0; to: 1 + duration: 600 + easing.type: Easing.OutQuad + } + readonly property string wanted: "file://" + Theme.wallImage property bool bFront: false diff --git a/settings/quickshell/AuthView.qml b/settings/quickshell/AuthView.qml index e945b27..9e2ced5 100644 --- a/settings/quickshell/AuthView.qml +++ b/settings/quickshell/AuthView.qml @@ -194,6 +194,23 @@ Item { Keys.onEscapePressed: text = "" } + // Spinner while PAM checks the password (and, in the greeter, + // through the fade-out into the session). + Text { + anchors.centerIn: parent + visible: view.busy + text: "loader-circle" + font.family: view.theme.iconFont + font.pixelSize: 20 + color: view.theme.accent + RotationAnimation on rotation { + running: view.busy + from: 0; to: 360 + duration: 900 + loops: Animation.Infinite + } + } + // Visible confirm button (after TextInput: on top) Rectangle { anchors.right: parent.right diff --git a/settings/quickshell/Greeter.qml b/settings/quickshell/Greeter.qml index 9452af8..1e08ec8 100644 --- a/settings/quickshell/Greeter.qml +++ b/settings/quickshell/Greeter.qml @@ -19,6 +19,8 @@ Window { password = ""; busy = false; errorMessage = message; + handoff.stop(); + veil.opacity = 0; } Connections { target: Greetd @@ -38,13 +40,25 @@ Window { function onError(error) { root.failed("Login unavailable; try again"); } function onReadyToLaunch() { root.password = ""; - // PAM/logind must know this is a graphical session before the - // compositor starts, including for the pre-suspend lock check. - Greetd.launch([Quickshell.env("FRED_SESSION_COMMAND")], [ - "XDG_SESSION_TYPE=wayland", - "XDG_CURRENT_DESKTOP=Hyprland", - "XDG_SESSION_DESKTOP=Hyprland" - ]); + handoff.start(); + } + } + // Fade to black before greetd swaps compositors. Hyprland starts on + // black and Quickshell fades the wallpaper in, so the VT gap between + // the two hides inside the dark instead of flashing. + SequentialAnimation { + id: handoff + NumberAnimation { target: veil; property: "opacity"; to: 1; duration: 350; easing.type: Easing.InQuad } + ScriptAction { + script: { + // PAM/logind must know this is a graphical session before the + // compositor starts, including for the pre-suspend lock check. + Greetd.launch([Quickshell.env("FRED_SESSION_COMMAND")], [ + "XDG_SESSION_TYPE=wayland", + "XDG_CURRENT_DESKTOP=Hyprland", + "XDG_SESSION_DESKTOP=Hyprland" + ]); + } } } AuthView { @@ -64,4 +78,5 @@ Window { Greetd.createSession("fred"); } } + Rectangle { id: veil; anchors.fill: parent; color: "black"; opacity: 0 } } diff --git a/settings/theming.nix b/settings/theming.nix index 7296d73..e49ecd9 100644 --- a/settings/theming.nix +++ b/settings/theming.nix @@ -304,7 +304,6 @@ let + ''["col.border_locked_active"]="rgb(${s.base05})",'' + ''groupbar={text_color="rgb(${s.base05})",'' + ''["col.active"]="rgb(${ink4})",["col.inactive"]="rgb(${ink3})"}}'') - ''misc={background_color="rgb(${s.base00})"}'' ] + "})"); # ── App icons ──────────────────────────────────────────────────────────