diff --git a/lockscreen/AuthContext.qml b/lockscreen/AuthContext.qml new file mode 100644 index 0000000..f5c5745 --- /dev/null +++ b/lockscreen/AuthContext.qml @@ -0,0 +1,43 @@ +import QtQuick +import Quickshell +import Quickshell.Io + +QtObject { + property int status: AuthContext.Status.FirstLogin + signal unlocked(); + + enum Status { + FirstLogin, + Authenticating, + LoginFailed + } + + property string password + + property var pamtester: Process { + property bool failed: true + + command: ["pamtester", "login", Quickshell.env("USER"), "authenticate"] + + onStarted: this.write(`${password}\n`) + + stdout: SplitParser { + // fails go to stderr + onRead: pamtester.failed = false + } + + onExited: { + if (failed) { + status = AuthContext.Status.LoginFailed; + } else { + unlocked(); + } + } + } + + function tryLogin(password: string) { + this.password = password + status = AuthContext.Status.Authenticating; + pamtester.running = true; + } +} diff --git a/lockscreen/Lockscreen.qml b/lockscreen/Lockscreen.qml new file mode 100644 index 0000000..83693f1 --- /dev/null +++ b/lockscreen/Lockscreen.qml @@ -0,0 +1,51 @@ +import QtQuick +import QtQuick.Controls.Basic + +Item { + required property AuthContext context + + Item { + anchors.centerIn: parent + scale: 2 + + TextField { + id: entryBox + anchors.centerIn: parent + width: 300 + + enabled: context.status != AuthContext.Status.Authenticating + focus: true + horizontalAlignment: TextInput.AlignHCenter + echoMode: TextInput.Password + inputMethodHints: Qt.ImhSensitiveData + placeholderText: "Enter password" + + onAccepted: { + if (text != "") context.tryLogin(text) + } + + onEnabledChanged: { + if (enabled) text = "" + } + } + + Text { + id: status + color: "white" + + anchors { + horizontalCenter: entryBox.horizontalCenter + top: entryBox.bottom + topMargin: 20 + } + + text: { + switch (context.status) { + case AuthContext.Status.FirstLogin: return "" + case AuthContext.Status.Authenticating: return "Authenticating" + case AuthContext.Status.LoginFailed: return "Login Failed" + } + } + } + } +} diff --git a/lockscreen/README.md b/lockscreen/README.md new file mode 100644 index 0000000..dfed773 --- /dev/null +++ b/lockscreen/README.md @@ -0,0 +1,10 @@ +# Lockscreen + +This is a barebones lockscreen with a password input box. +Note that you MUST have `pamtester` installed or you won't be able to log in. + +You can run the lockscreen with `quickshell -c shell.qml`. + +You can run the lockscreen in test mode (as a window) with `quickshell -c test.qml`. + +![](./image.png) diff --git a/lockscreen/image.png b/lockscreen/image.png new file mode 100644 index 0000000..956c8aa Binary files /dev/null and b/lockscreen/image.png differ diff --git a/lockscreen/shell.qml b/lockscreen/shell.qml new file mode 100644 index 0000000..2a36ac8 --- /dev/null +++ b/lockscreen/shell.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Controls.Basic +import Quickshell +import Quickshell.Wayland + +ShellRoot { + AuthContext { + id: authContext + onUnlocked: lock.locked = false + } + + SessionLock { + id: lock + locked: true + + onLockedChanged: { + if (!locked) Qt.quit(); + } + + SessionLockSurface { + // You probably want to replace this with an image. + color: "#303030" + + // For your own sanity you should probably keep this + // while working on the lockscreen. + Button { + text: "Help! I misconfigured my lockscreen!" + onClicked: lock.locked = false + } + + Lockscreen { + anchors.fill: parent + context: authContext + } + } + } + +} diff --git a/lockscreen/test.qml b/lockscreen/test.qml new file mode 100644 index 0000000..2917665 --- /dev/null +++ b/lockscreen/test.qml @@ -0,0 +1,18 @@ +import QtQuick +import Quickshell + +ShellRoot { + AuthContext { + id: authContext + onUnlocked: Qt.quit() + } + + FloatingWindow { + color: "#303030" + + Lockscreen { + anchors.fill: parent + context: authContext + } + } +}