lock: add fprint support (#429)
* lock: add fprint support * lock: better fprint detection * lock: cap error retries * nix: fix fprint pam for nix * lock: reset fprint tries * lock: minor pam fixes Delay fprint error retries Reset fprint error retries on lock * lock: loading indicator passwd state Instead of fprint state cause no way of detecting that * dashboard: better visualiser * lock: better fprint availability check * lock: better in/out anim Animating layout sizes is a bad idea :woe: Use scale instead * lock: add better error/fail messages * lock: less fprint icon states Already shown by message * lock: fix fprint reset * lock: include passwd pam * lock: flash message on change * lock: fix message anim Also wrap message instead of eliding * lock: better messages for no fprint
This commit is contained in:
parent
f747d82b93
commit
aea5795832
11 changed files with 311 additions and 29 deletions
3
assets/pam.d/fprint
Normal file
3
assets/pam.d/fprint
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#%PAM-1.0
|
||||||
|
|
||||||
|
auth required pam_fprintd.so max-tries=1
|
||||||
6
assets/pam.d/passwd
Normal file
6
assets/pam.d/passwd
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#%PAM-1.0
|
||||||
|
|
||||||
|
auth required pam_faillock.so preauth
|
||||||
|
auth [success=1 default=bad] pam_unix.so nullok
|
||||||
|
auth [default=die] pam_faillock.so authfail
|
||||||
|
auth required pam_faillock.so authsucc
|
||||||
|
|
@ -2,6 +2,8 @@ import Quickshell.Io
|
||||||
|
|
||||||
JsonObject {
|
JsonObject {
|
||||||
property bool recolourLogo: false
|
property bool recolourLogo: false
|
||||||
|
property bool enableFprint: true
|
||||||
|
property int maxFprintTries: 3
|
||||||
property Sizes sizes: Sizes {}
|
property Sizes sizes: Sizes {}
|
||||||
|
|
||||||
component Sizes: JsonObject {
|
component Sizes: JsonObject {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import qs.components
|
import qs.components
|
||||||
|
import qs.components.controls
|
||||||
import qs.components.images
|
import qs.components.images
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.config
|
import qs.config
|
||||||
|
|
@ -17,6 +18,7 @@ ColumnLayout {
|
||||||
readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
|
readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
|
||||||
|
|
||||||
Layout.preferredWidth: centerWidth
|
Layout.preferredWidth: centerWidth
|
||||||
|
Layout.fillWidth: false
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
spacing: Appearance.spacing.large * 2
|
spacing: Appearance.spacing.large * 2
|
||||||
|
|
@ -148,9 +150,34 @@ ColumnLayout {
|
||||||
anchors.margins: Appearance.padding.small
|
anchors.margins: Appearance.padding.small
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
MaterialIcon {
|
Item {
|
||||||
Layout.leftMargin: Appearance.padding.smaller
|
implicitWidth: implicitHeight
|
||||||
text: "lock"
|
implicitHeight: fprintIcon.implicitHeight + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
id: fprintIcon
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
animate: true
|
||||||
|
text: {
|
||||||
|
if (root.lock.pam.fprint.tries >= Config.lock.maxFprintTries)
|
||||||
|
return "fingerprint_off";
|
||||||
|
if (root.lock.pam.fprint.active)
|
||||||
|
return "fingerprint";
|
||||||
|
return "lock";
|
||||||
|
}
|
||||||
|
color: root.lock.pam.fprint.tries >= Config.lock.maxFprintTries ? Colours.palette.m3error : Colours.palette.m3onSurface
|
||||||
|
opacity: root.lock.pam.passwd.active ? 0 : 1
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledBusyIndicator {
|
||||||
|
anchors.fill: parent
|
||||||
|
running: root.lock.pam.passwd.active
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputField {
|
InputField {
|
||||||
|
|
@ -186,6 +213,135 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: message
|
||||||
|
|
||||||
|
readonly property Pam pam: root.lock.pam
|
||||||
|
readonly property string msg: {
|
||||||
|
if (pam.fprintState === "error")
|
||||||
|
return qsTr("ERROR: %1").arg(pam.fprint.message);
|
||||||
|
if (pam.state === "error")
|
||||||
|
return qsTr("ERROR: %1").arg(pam.passwd.message);
|
||||||
|
|
||||||
|
if (pam.lockMessage)
|
||||||
|
return pam.lockMessage;
|
||||||
|
|
||||||
|
if (pam.state === "max" && pam.fprintState === "max")
|
||||||
|
return qsTr("Maximum password and fingerprint attempts reached.");
|
||||||
|
if (pam.state === "max") {
|
||||||
|
if (pam.fprint.available)
|
||||||
|
return qsTr("Maximum password attempts reached. Please use fingerprint.");
|
||||||
|
return qsTr("Maximum password attempts reached.");
|
||||||
|
}
|
||||||
|
if (pam.fprintState === "max")
|
||||||
|
return qsTr("Maximum fingerprint attempts reached. Please use password.");
|
||||||
|
|
||||||
|
if (pam.state === "fail") {
|
||||||
|
if (pam.fprint.available)
|
||||||
|
return qsTr("Incorrect password. Please try again or use fingerprint.");
|
||||||
|
return qsTr("Incorrect password. Please try again.");
|
||||||
|
}
|
||||||
|
if (pam.fprintState === "fail")
|
||||||
|
return qsTr("Fingerprint not recognized (%1/%2). Please try again or use password.").arg(pam.fprint.tries).arg(Config.lock.maxFprintTries);
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: -Appearance.spacing.large
|
||||||
|
|
||||||
|
scale: 0.7
|
||||||
|
opacity: 0
|
||||||
|
color: Colours.palette.m3error
|
||||||
|
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
font.family: Appearance.font.family.mono
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
|
|
||||||
|
onMsgChanged: {
|
||||||
|
if (msg) {
|
||||||
|
if (opacity > 0) {
|
||||||
|
animate = true;
|
||||||
|
text = msg;
|
||||||
|
animate = false;
|
||||||
|
|
||||||
|
exitAnim.stop();
|
||||||
|
if (scale < 1)
|
||||||
|
appearAnim.restart();
|
||||||
|
else
|
||||||
|
flashAnim.restart();
|
||||||
|
} else {
|
||||||
|
text = msg;
|
||||||
|
exitAnim.stop();
|
||||||
|
appearAnim.restart();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
appearAnim.stop();
|
||||||
|
flashAnim.stop();
|
||||||
|
exitAnim.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.lock.pam
|
||||||
|
|
||||||
|
function onFlashMsg(): void {
|
||||||
|
exitAnim.stop();
|
||||||
|
if (message.scale < 1)
|
||||||
|
appearAnim.restart();
|
||||||
|
else
|
||||||
|
flashAnim.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
id: appearAnim
|
||||||
|
|
||||||
|
target: message
|
||||||
|
properties: "scale,opacity"
|
||||||
|
to: 1
|
||||||
|
onFinished: flashAnim.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: flashAnim
|
||||||
|
|
||||||
|
loops: 2
|
||||||
|
|
||||||
|
MessageAnim {
|
||||||
|
to: 0.3
|
||||||
|
}
|
||||||
|
MessageAnim {
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: exitAnim
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
target: message
|
||||||
|
property: "scale"
|
||||||
|
to: 0.7
|
||||||
|
duration: Appearance.anim.durations.large
|
||||||
|
}
|
||||||
|
Anim {
|
||||||
|
target: message
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
duration: Appearance.anim.durations.large
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component MessageAnim: NumberAnimation {
|
||||||
|
target: message
|
||||||
|
property: "opacity"
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
|
||||||
component Anim: NumberAnimation {
|
component Anim: NumberAnimation {
|
||||||
duration: Appearance.anim.durations.normal
|
duration: Appearance.anim.durations.normal
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,12 @@ RowLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property var lock
|
required property var lock
|
||||||
property real centerScale
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Appearance.padding.large
|
|
||||||
|
|
||||||
spacing: Appearance.spacing.large * 2
|
spacing: Appearance.spacing.large * 2
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
scale: root.centerScale
|
|
||||||
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
@ -62,16 +57,12 @@ RowLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
Center {
|
Center {
|
||||||
Layout.leftMargin: -(1 - scale) * implicitWidth / 2
|
|
||||||
Layout.rightMargin: -(1 - scale) * implicitWidth / 2
|
|
||||||
scale: root.centerScale
|
|
||||||
lock: root.lock
|
lock: root.lock
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
scale: root.centerScale
|
|
||||||
|
|
||||||
StyledRect {
|
StyledRect {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
|
||||||
|
|
@ -40,19 +40,15 @@ Item {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
text: {
|
text: {
|
||||||
if (root.pam.active)
|
if (root.pam.passwd.active)
|
||||||
return qsTr("Loading...");
|
return qsTr("Loading...");
|
||||||
if (root.pam.state === "error")
|
|
||||||
return qsTr("An error occured");
|
|
||||||
if (root.pam.state === "max")
|
if (root.pam.state === "max")
|
||||||
return qsTr("You have reached the maximum number of tries");
|
return qsTr("You have reached the maximum number of tries");
|
||||||
if (root.pam.state === "fail")
|
|
||||||
return qsTr("Incorrect password");
|
|
||||||
return qsTr("Enter your password");
|
return qsTr("Enter your password");
|
||||||
}
|
}
|
||||||
|
|
||||||
animate: true
|
animate: true
|
||||||
color: root.pam.active ? Colours.palette.m3secondary : root.pam.state ? Colours.palette.m3error : Colours.palette.m3outline
|
color: root.pam.passwd.active ? Colours.palette.m3secondary : Colours.palette.m3outline
|
||||||
font.pointSize: Appearance.font.size.normal
|
font.pointSize: Appearance.font.size.normal
|
||||||
font.family: Appearance.font.family.mono
|
font.family: Appearance.font.family.mono
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ WlSessionLockSurface {
|
||||||
required property Pam pam
|
required property Pam pam
|
||||||
|
|
||||||
readonly property alias unlocking: unlockAnim.running
|
readonly property alias unlocking: unlockAnim.running
|
||||||
readonly property bool animating: initAnim.running || unlockAnim.running
|
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
|
|
@ -44,7 +43,7 @@ WlSessionLockSurface {
|
||||||
}
|
}
|
||||||
Anim {
|
Anim {
|
||||||
target: content
|
target: content
|
||||||
property: "centerScale"
|
property: "scale"
|
||||||
to: 0
|
to: 0
|
||||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||||
|
|
@ -132,7 +131,7 @@ WlSessionLockSurface {
|
||||||
}
|
}
|
||||||
Anim {
|
Anim {
|
||||||
target: content
|
target: content
|
||||||
property: "centerScale"
|
property: "scale"
|
||||||
to: 1
|
to: 1
|
||||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||||
|
|
@ -219,8 +218,13 @@ WlSessionLockSurface {
|
||||||
Content {
|
Content {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio - Appearance.padding.large * 2
|
||||||
|
height: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult - Appearance.padding.large * 2
|
||||||
|
|
||||||
lock: root
|
lock: root
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
scale: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ Item {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: root.lock.animating ? "" : (Players.active?.trackArtUrl ?? "")
|
source: Players.active?.trackArtUrl ?? ""
|
||||||
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ ColumnLayout {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
active: opacity > 0
|
active: opacity > 0
|
||||||
opacity: root.lock.animating || Notifs.list.length > 0 ? 0 : 1
|
opacity: Notifs.list.length > 0 ? 0 : 1
|
||||||
|
|
||||||
sourceComponent: ColumnLayout {
|
sourceComponent: ColumnLayout {
|
||||||
spacing: Appearance.spacing.large
|
spacing: Appearance.spacing.large
|
||||||
|
|
@ -51,7 +51,7 @@ ColumnLayout {
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
source: `file://${Quickshell.shellDir}/assets/dino.png`
|
source: `file://${Quickshell.shellDir}/assets/dino.png`
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
sourceSize.width: root.lock.animating ? 0 : clipRect.width * 0.8
|
sourceSize.width: clipRect.width * 0.8
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: Colouriser {
|
layer.effect: Colouriser {
|
||||||
|
|
@ -84,7 +84,7 @@ ColumnLayout {
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: root.lock.animating ? [] : [...new Set(Notifs.list.map(notif => notif.appName))].reverse()
|
values: [...new Set(Notifs.list.map(notif => notif.appName))].reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: NotifGroup {}
|
delegate: NotifGroup {}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
import qs.config
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Services.Pam
|
import Quickshell.Services.Pam
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
@ -9,12 +11,16 @@ Scope {
|
||||||
required property WlSessionLock lock
|
required property WlSessionLock lock
|
||||||
|
|
||||||
readonly property alias passwd: passwd
|
readonly property alias passwd: passwd
|
||||||
readonly property bool active: passwd.active
|
readonly property alias fprint: fprint
|
||||||
|
property string lockMessage
|
||||||
property string state
|
property string state
|
||||||
|
property string fprintState
|
||||||
property string buffer
|
property string buffer
|
||||||
|
|
||||||
|
signal flashMsg
|
||||||
|
|
||||||
function handleKey(event: KeyEvent): void {
|
function handleKey(event: KeyEvent): void {
|
||||||
if (passwd.active)
|
if (passwd.active || state === "max")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
|
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
|
||||||
|
|
@ -34,6 +40,16 @@ Scope {
|
||||||
PamContext {
|
PamContext {
|
||||||
id: passwd
|
id: passwd
|
||||||
|
|
||||||
|
config: "passwd"
|
||||||
|
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||||
|
|
||||||
|
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: {
|
onResponseRequiredChanged: {
|
||||||
if (!responseRequired)
|
if (!responseRequired)
|
||||||
return;
|
return;
|
||||||
|
|
@ -53,22 +69,125 @@ Scope {
|
||||||
else if (res === PamResult.Failed)
|
else if (res === PamResult.Failed)
|
||||||
root.state = "fail";
|
root.state = "fail";
|
||||||
|
|
||||||
|
root.flashMsg();
|
||||||
stateReset.restart();
|
stateReset.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PamContext {
|
||||||
|
id: fprint
|
||||||
|
|
||||||
|
property bool available
|
||||||
|
property int tries
|
||||||
|
property int errorTries
|
||||||
|
|
||||||
|
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 {
|
Timer {
|
||||||
id: stateReset
|
id: stateReset
|
||||||
|
|
||||||
interval: 4000
|
interval: 4000
|
||||||
onTriggered: root.state = ""
|
onTriggered: {
|
||||||
|
if (root.state !== "max")
|
||||||
|
root.state = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: fprintStateReset
|
||||||
|
|
||||||
|
interval: 4000
|
||||||
|
onTriggered: {
|
||||||
|
root.fprintState = "";
|
||||||
|
fprint.errorTries = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.lock
|
target: root.lock
|
||||||
|
|
||||||
|
function onSecureChanged(): void {
|
||||||
|
if (root.lock.secure) {
|
||||||
|
availProc.running = true;
|
||||||
|
root.buffer = "";
|
||||||
|
root.state = "";
|
||||||
|
root.fprintState = "";
|
||||||
|
root.lockMessage = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onUnlock(): void {
|
function onUnlock(): void {
|
||||||
root.buffer = "";
|
fprint.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Config.lock
|
||||||
|
|
||||||
|
function onEnableFprintChanged(): void {
|
||||||
|
fprint.checkAvail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,11 @@
|
||||||
nativeBuildInputs = [gcc];
|
nativeBuildInputs = [gcc];
|
||||||
buildInputs = [aubio pipewire];
|
buildInputs = [aubio pipewire];
|
||||||
|
|
||||||
|
patchPhase = ''
|
||||||
|
substituteInPlace assets/pam.d/fprint \
|
||||||
|
--replace-fail pam_fprintd.so /run/current-system/sw/lib/security/pam_fprintd.so
|
||||||
|
'';
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
g++ -std=c++17 -Wall -Wextra \
|
g++ -std=c++17 -Wall -Wextra \
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue