lockscreen: replace hyprlock with quickshell WlSessionLock
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
24123270b2
commit
bedb11930f
2 changed files with 162 additions and 54 deletions
|
|
@ -52,7 +52,6 @@ in
|
|||
brightnessctl
|
||||
swayosd
|
||||
playerctl
|
||||
hyprlock
|
||||
hypridle
|
||||
hyprshot
|
||||
hyprpicker
|
||||
|
|
@ -161,6 +160,9 @@ in
|
|||
focus_on_activate = false;
|
||||
vrr = 2;
|
||||
background_color = rgb c.base00;
|
||||
# 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;
|
||||
};
|
||||
|
||||
# vfr moved from misc: to debug: in 0.55.0
|
||||
|
|
@ -340,56 +342,8 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
programs.hyprlock = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general = {
|
||||
grace = 5;
|
||||
hide_cursor = true;
|
||||
};
|
||||
background = {
|
||||
blur_passes = 3;
|
||||
blur_size = 8;
|
||||
brightness = 0.6;
|
||||
};
|
||||
input-field = {
|
||||
size = "280, 44";
|
||||
outline_thickness = 2;
|
||||
dots_size = 0.25;
|
||||
dots_spacing = 0.2;
|
||||
dots_center = true;
|
||||
fade_on_empty = true;
|
||||
fade_timeout = 3000;
|
||||
placeholder_text = "";
|
||||
fail_text = "";
|
||||
position = "0, -40";
|
||||
halign = "center";
|
||||
valign = "center";
|
||||
rounding = 8;
|
||||
};
|
||||
label = [
|
||||
{
|
||||
text = "$TIME";
|
||||
color = "rgb(${c.base05})";
|
||||
font_size = 72;
|
||||
font_family = "Inter";
|
||||
position = "0, 120";
|
||||
halign = "center";
|
||||
valign = "center";
|
||||
}
|
||||
{
|
||||
text = "cmd[update:60000] date +'%A, %d %B'";
|
||||
color = "rgb(${c.base04})";
|
||||
font_size = 16;
|
||||
font_family = "Inter";
|
||||
position = "0, 60";
|
||||
halign = "center";
|
||||
valign = "center";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Lock screen is quickshell's WlSessionLock (settings/quickshell.nix),
|
||||
# triggered via `qs ipc call lock lock`.
|
||||
services.hypridle = lib.mkIf isMacbook (
|
||||
let
|
||||
# Skip the action if any MPRIS player is playing — covers windowed
|
||||
|
|
@ -400,7 +354,7 @@ in
|
|||
enable = true;
|
||||
settings = {
|
||||
general = {
|
||||
lock_cmd = "pidof hyprlock || hyprlock";
|
||||
lock_cmd = "qs ipc call lock lock";
|
||||
before_sleep_cmd = "loginctl lock-session";
|
||||
after_sleep_cmd = "hyprctl dispatch dpms on";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ in
|
|||
singleton Theme 1.0 Theme.qml
|
||||
singleton Commands 1.0 Commands.qml
|
||||
Bar 1.0 Bar.qml
|
||||
Lock 1.0 Lock.qml
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
@ -191,6 +192,150 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
"quickshell/Lock.qml" = {
|
||||
onChange = qsRestart;
|
||||
text = ''
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.Pam
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
// ext-session-lock screen, replacing hyprlock. Lives in the shell
|
||||
// process: set `locked: true` and the compositor hands every
|
||||
// screen to a LockSurface below until PAM says otherwise.
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
|
||||
property string password: ""
|
||||
property bool failed: false
|
||||
|
||||
function submit(pw) {
|
||||
if (pam.active || pw === "") return;
|
||||
failed = false;
|
||||
password = pw;
|
||||
pam.start();
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: pam
|
||||
// NixOS ships a "login" pam service; pam_unix verifies the
|
||||
// user's own password via the unix_chkpwd setuid wrapper.
|
||||
config: "login"
|
||||
onPamMessage: {
|
||||
if (this.responseRequired) this.respond(lock.password);
|
||||
}
|
||||
onCompleted: result => {
|
||||
lock.password = "";
|
||||
if (result === PamResult.Success) lock.locked = false;
|
||||
else lock.failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: surface
|
||||
color: "black"
|
||||
|
||||
Image {
|
||||
id: wall
|
||||
anchors.fill: parent
|
||||
source: "file://" + Quickshell.env("HOME") + "/.local/share/backgrounds/wallpaper.png"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
visible: false
|
||||
}
|
||||
FastBlur {
|
||||
anchors.fill: wall
|
||||
source: wall
|
||||
radius: 48
|
||||
}
|
||||
Rectangle { anchors.fill: parent; color: "black"; opacity: 0.4 }
|
||||
|
||||
SystemClock {
|
||||
id: lockClock
|
||||
precision: SystemClock.Minutes
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -60
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: lockClock.date.toLocaleTimeString(Qt.locale(), "HH:mm")
|
||||
color: Theme.base05
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 72
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: lockClock.date.toLocaleDateString(Qt.locale(), "dddd, d MMMM")
|
||||
color: Theme.base04
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 16
|
||||
}
|
||||
|
||||
Item { width: 1; height: 32 }
|
||||
|
||||
// Password pill: dots for typed chars; border tracks
|
||||
// state (accent while checking, red on failure).
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 280
|
||||
height: 44
|
||||
radius: Theme.radius
|
||||
color: Theme.cardBg
|
||||
border.width: Theme.borderWidth
|
||||
border.color: pam.active ? Theme.base0D
|
||||
: lock.failed ? Theme.base08
|
||||
: Theme.base03
|
||||
Behavior on border.color { ColorAnimation { duration: Theme.animFade } }
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
Repeater {
|
||||
model: Math.min(pwInput.text.length, 24)
|
||||
Rectangle {
|
||||
width: 8; height: 8; radius: 4
|
||||
color: Theme.base05
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
visible: pwInput.text === "" && lock.failed
|
||||
text: "wrong password"
|
||||
color: Theme.base08
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: pwInput
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
enabled: !pam.active
|
||||
echoMode: TextInput.Password
|
||||
focus: true
|
||||
onAccepted: {
|
||||
lock.submit(text);
|
||||
text = "";
|
||||
}
|
||||
Keys.onEscapePressed: text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: pwInput.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
"quickshell/Commands.qml" = {
|
||||
onChange = qsRestart;
|
||||
text = ''
|
||||
|
|
@ -201,7 +346,6 @@ in
|
|||
readonly property string nmcli: "${nmcli}"
|
||||
readonly property string wifiConnect: "${wifiConnectScript}"
|
||||
readonly property string notifSound: "${pkgs.libcanberra-gtk3}/bin/canberra-gtk-play"
|
||||
readonly property string hyprlock: "${pkgs.hyprlock}/bin/hyprlock"
|
||||
readonly property string systemctl: "${pkgs.systemd}/bin/systemctl"
|
||||
readonly property string weatherFetch: "${weatherFetchScript}"
|
||||
}
|
||||
|
|
@ -239,6 +383,16 @@ in
|
|||
function reload(): void { Quickshell.reload(false); }
|
||||
}
|
||||
|
||||
// Session lock (replaces hyprlock). `qs ipc call lock lock`
|
||||
// from hypridle / loginctl lock-session; the session menu
|
||||
// calls root.lock() directly. Idempotent while locked.
|
||||
Lock { id: lockScreen }
|
||||
function lock() { lockScreen.locked = true; }
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
function lock(): void { root.lock(); }
|
||||
}
|
||||
|
||||
// Screenshot pin: the Shift+Super+S keybind brackets hyprshot
|
||||
// with pin/unpin so the focus grab is suspended while slurp
|
||||
// grabs input — otherwise the open menu would close like any
|
||||
|
|
@ -639,7 +793,7 @@ in
|
|||
|
||||
function activate(act) {
|
||||
open = false;
|
||||
if (act === "lock") Quickshell.execDetached([Commands.hyprlock]);
|
||||
if (act === "lock") bar.shellRoot.lock();
|
||||
else if (act === "logout") Hyprland.dispatch("hl.dsp.exit()");
|
||||
else if (act === "reboot") Quickshell.execDetached([Commands.systemctl, "reboot"]);
|
||||
else Quickshell.execDetached([Commands.systemctl, "poweroff"]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue