diff --git a/Lock.qml b/Lock.qml new file mode 100644 index 0000000..f7c263d --- /dev/null +++ b/Lock.qml @@ -0,0 +1,169 @@ +import Quickshell +import Quickshell.Wayland +import Quickshell.Widgets +import Quickshell.Io +import Quickshell.Services.Pam +import QtQuick +import QtQuick.Controls +import QtQuick.Effects + +WlSessionLock { + id: lock + locked: false + required property bool animate + WlSessionLockSurface { + id: surface + color: "transparent" + + Rectangle { + id: content + anchors.fill: parent + visible: content.ready + readonly property bool ready: + lock.animate && + lock.secure && + capture.hasContent + color: "#181818" + Text { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: surface.height/12 + } + color: "white" + font.pixelSize: 160 + font.family: "Sriracha" + property var clock: SystemClock {} + text: `${clock.hours}:${clock.minutes}` + } + Rectangle { + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + bottomMargin: surface.height/6 + } + width: 300 + height: 40 + radius: height/2 + color: "#222222" + TextInput { + id: input + x: (parent.width - this.width)/2 + Behavior on x { NumberAnimation { + duration: 300 + easing.type: Easing.OutQuint + }} + anchors.verticalCenter: parent.verticalCenter + focus: true + color: "white" + font.pixelSize: 20 + echoMode: TextInput.Password + cursorDelegate: Item {} + Keys.onReturnPressed: pam.start() + } + Text { + anchors { + horizontalCenter: input.horizontalCenter + top: parent.bottom + topMargin: 10 + } + color: "white" + font.pixelSize: 14 + text: pam.message + } + } + } + ClippingRectangle { + anchors.fill: parent + color: "transparent" + radius: 0 + RectangularShadow { + visible: content.ready + anchors.fill: parent + spread: 2 + blur: 10 + } + ScreencopyView { + id: capture + anchors.fill: parent + captureSource: surface.screen + Connections { + target: lock + function onLockedChanged() {capture.captureFrame()} + } + } + transform: Scale { + origin { x: width/2; y: height/2 } + xScale: 1 + yScale: xScale + NumberAnimation on xScale { + running: content.ready + to: 0.85 + duration: 800 + easing.type: Easing.InQuint + } + NumberAnimation on xScale { + id: exit + running: false + from: 0.85 + to: 1 + duration: 800 + easing.type: Easing.OutQuint + onFinished: { + content.visible = false + fadeOut.start() + } + } + } + SequentialAnimation on opacity { + running: content.ready + PauseAnimation {duration: 500} + NumberAnimation { + duration: 300 + to: 0 + } + } + NumberAnimation on opacity { + duration: 300 + running: exit.running + from: 0 + to: 1 + } + NumberAnimation on radius { + running: content.ready + duration: 800 + to: 32 + } + NumberAnimation on radius { + running: exit.running + to: 0 + duration: 800 + } + NumberAnimation on opacity { + id: fadeOut + running: false + to: 0 + duration: 150 + onFinished: lock.locked = false + } + } + PamContext { + id: pam + config: "pam.conf" + configDirectory: "." + onPamMessage: if (this.responseRequired) this.respond(input.text) + onCompleted: result => { + switch (result) { + case PamResult.Success: + exit.start() + break; + case PamResult.Failed: + case PamResult.Error: + case PamResult.MaxTries: + input.clear() + fail.start(); + break; + }} + } + } +} diff --git a/pam.conf b/pam.conf new file mode 100644 index 0000000..cc1b363 --- /dev/null +++ b/pam.conf @@ -0,0 +1,2 @@ +auth sufficient pam_unix.so try_first_pass likeauth nullok +auth sufficient pam_fprintd.so diff --git a/shell.qml b/shell.qml index b0644e4..1c48d37 100644 --- a/shell.qml +++ b/shell.qml @@ -1,8 +1,19 @@ import Quickshell +import Quickshell.Io import "." as Shell ShellRoot { Shell.Wall {} Shell.Launcher {} Shell.Boateye {} + Shell.Lock { + id: lock + animate: true + } + IpcHandler { + target: "lock" + function instalock() {lock.animate = false; lock.locked = true} + function open() {lock.animate = true; lock.locked = true} + function close() {lock.locked = false} + } }