Files
Zacharias-Brohn d56a0260fb formatter
2026-02-24 23:20:11 +01:00

195 lines
3.6 KiB
QML

import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Services.Pam
import QtQuick
import qs.Config
Scope {
id: root
property string buffer
readonly property alias fprint: fprint
property string fprintState
required property WlSessionLock lock
property string lockMessage
readonly property alias passwd: passwd
property string state
signal flashMsg
function handleKey(event: KeyEvent): void {
if (passwd.active || state === "max")
return;
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
passwd.start();
} else if (event.key === Qt.Key_Backspace) {
if (event.modifiers & Qt.ControlModifier) {
buffer = "";
} else {
buffer = buffer.slice(0, -1);
}
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
// No illegal characters (you are insane if you use unicode in your password)
buffer += event.text;
}
}
PamContext {
id: passwd
config: "passwd"
configDirectory: Quickshell.shellDir + "/assets/pam.d"
onCompleted: res => {
if (res === PamResult.Success)
return root.lock.unlock();
if (res === PamResult.Error)
root.state = "error";
else if (res === PamResult.MaxTries)
root.state = "max";
else if (res === PamResult.Failed)
root.state = "fail";
root.flashMsg();
stateReset.restart();
}
onMessageChanged: {
if (message.startsWith("The account is locked"))
root.lockMessage = message;
else if (root.lockMessage && message.endsWith(" left to unlock)"))
root.lockMessage += "\n" + message;
}
onResponseRequiredChanged: {
if (!responseRequired)
return;
respond(root.buffer);
root.buffer = "";
}
}
PamContext {
id: fprint
property bool available
property int errorTries
property int tries
function checkAvail(): void {
if (!available || !Config.lock.enableFprint || !root.lock.secure) {
abort();
return;
}
tries = 0;
errorTries = 0;
start();
}
config: "fprint"
configDirectory: Quickshell.shellDir + "/assets/pam.d"
onCompleted: res => {
if (!available)
return;
if (res === PamResult.Success)
return root.lock.unlock();
if (res === PamResult.Error) {
root.fprintState = "error";
errorTries++;
if (errorTries < 5) {
abort();
errorRetry.restart();
}
} else if (res === PamResult.MaxTries) {
// Isn't actually the real max tries as pam only reports completed
// when max tries is reached.
tries++;
if (tries < Config.lock.maxFprintTries) {
// Restart if not actually real max tries
root.fprintState = "fail";
start();
} else {
root.fprintState = "max";
abort();
}
}
root.flashMsg();
fprintStateReset.start();
}
}
Process {
id: availProc
command: ["sh", "-c", "fprintd-list $USER"]
onExited: code => {
fprint.available = code === 0;
fprint.checkAvail();
}
}
Timer {
id: errorRetry
interval: 800
onTriggered: fprint.start()
}
Timer {
id: stateReset
interval: 4000
onTriggered: {
if (root.state !== "max")
root.state = "";
}
}
Timer {
id: fprintStateReset
interval: 4000
onTriggered: {
root.fprintState = "";
fprint.errorTries = 0;
}
}
Connections {
function onSecureChanged(): void {
if (root.lock.secure) {
availProc.running = true;
root.buffer = "";
root.state = "";
root.fprintState = "";
root.lockMessage = "";
}
}
function onUnlock(): void {
fprint.abort();
}
target: root.lock
}
Connections {
function onEnableFprintChanged(): void {
fprint.checkAvail();
}
target: Config.lock
}
}