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
|
brightnessctl
|
||||||
swayosd
|
swayosd
|
||||||
playerctl
|
playerctl
|
||||||
hyprlock
|
|
||||||
hypridle
|
hypridle
|
||||||
hyprshot
|
hyprshot
|
||||||
hyprpicker
|
hyprpicker
|
||||||
|
|
@ -161,6 +160,9 @@ in
|
||||||
focus_on_activate = false;
|
focus_on_activate = false;
|
||||||
vrr = 2;
|
vrr = 2;
|
||||||
background_color = rgb c.base00;
|
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
|
# vfr moved from misc: to debug: in 0.55.0
|
||||||
|
|
@ -340,56 +342,8 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.hyprlock = {
|
# Lock screen is quickshell's WlSessionLock (settings/quickshell.nix),
|
||||||
enable = true;
|
# triggered via `qs ipc call lock lock`.
|
||||||
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";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.hypridle = lib.mkIf isMacbook (
|
services.hypridle = lib.mkIf isMacbook (
|
||||||
let
|
let
|
||||||
# Skip the action if any MPRIS player is playing — covers windowed
|
# Skip the action if any MPRIS player is playing — covers windowed
|
||||||
|
|
@ -400,7 +354,7 @@ in
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
general = {
|
general = {
|
||||||
lock_cmd = "pidof hyprlock || hyprlock";
|
lock_cmd = "qs ipc call lock lock";
|
||||||
before_sleep_cmd = "loginctl lock-session";
|
before_sleep_cmd = "loginctl lock-session";
|
||||||
after_sleep_cmd = "hyprctl dispatch dpms on";
|
after_sleep_cmd = "hyprctl dispatch dpms on";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ in
|
||||||
singleton Theme 1.0 Theme.qml
|
singleton Theme 1.0 Theme.qml
|
||||||
singleton Commands 1.0 Commands.qml
|
singleton Commands 1.0 Commands.qml
|
||||||
Bar 1.0 Bar.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" = {
|
"quickshell/Commands.qml" = {
|
||||||
onChange = qsRestart;
|
onChange = qsRestart;
|
||||||
text = ''
|
text = ''
|
||||||
|
|
@ -201,7 +346,6 @@ in
|
||||||
readonly property string nmcli: "${nmcli}"
|
readonly property string nmcli: "${nmcli}"
|
||||||
readonly property string wifiConnect: "${wifiConnectScript}"
|
readonly property string wifiConnect: "${wifiConnectScript}"
|
||||||
readonly property string notifSound: "${pkgs.libcanberra-gtk3}/bin/canberra-gtk-play"
|
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 systemctl: "${pkgs.systemd}/bin/systemctl"
|
||||||
readonly property string weatherFetch: "${weatherFetchScript}"
|
readonly property string weatherFetch: "${weatherFetchScript}"
|
||||||
}
|
}
|
||||||
|
|
@ -239,6 +383,16 @@ in
|
||||||
function reload(): void { Quickshell.reload(false); }
|
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
|
// Screenshot pin: the Shift+Super+S keybind brackets hyprshot
|
||||||
// with pin/unpin so the focus grab is suspended while slurp
|
// with pin/unpin so the focus grab is suspended while slurp
|
||||||
// grabs input — otherwise the open menu would close like any
|
// grabs input — otherwise the open menu would close like any
|
||||||
|
|
@ -639,7 +793,7 @@ in
|
||||||
|
|
||||||
function activate(act) {
|
function activate(act) {
|
||||||
open = false;
|
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 === "logout") Hyprland.dispatch("hl.dsp.exit()");
|
||||||
else if (act === "reboot") Quickshell.execDetached([Commands.systemctl, "reboot"]);
|
else if (act === "reboot") Quickshell.execDetached([Commands.systemctl, "reboot"]);
|
||||||
else Quickshell.execDetached([Commands.systemctl, "poweroff"]);
|
else Quickshell.execDetached([Commands.systemctl, "poweroff"]);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue