lockscreen: replace lockscreen example

The new one uses the pam module and looks much nicer.
This commit is contained in:
outfoxxed 2024-06-18 17:14:51 -07:00
parent c30a4faf18
commit a64b477dea
Signed by: outfoxxed
GPG Key ID: 4C88A185FB89301E
9 changed files with 190 additions and 126 deletions

View File

@ -1,43 +0,0 @@
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;
}
}

View File

@ -0,0 +1,54 @@
import QtQuick
import Quickshell
import Quickshell.Services.Pam
Scope {
id: root
signal unlocked()
signal failed()
// These properties are in the context and not individual lock surfaces
// so all surfaces can share the same state.
property string currentText: ""
property bool unlockInProgress: false
property bool showFailure: false
// Clear the failure text once the user starts typing.
onCurrentTextChanged: showFailure = false;
function tryUnlock() {
if (currentText === "") return;
root.unlockInProgress = true;
pam.start();
}
PamContext {
id: pam
// Its best to have a custom pam config for quickshell, as the system one
// might not be what your interface expects, and break in some way.
// This particular example only supports passwords.
configDirectory: "pam"
config: "password.conf"
// pam_unix will ask for a response for the password prompt
onPamMessage: {
if (this.responseRequired) {
this.respond(root.currentText);
}
}
// pam_unix won't send any important messages so all we need is the completion status.
onCompleted: result => {
if (result == PamResult.Success) {
root.unlocked();
} else {
root.currentText = "";
root.showFailure = true;
}
root.unlockInProgress = false;
}
}
}

104
lockscreen/LockSurface.qml Normal file
View File

@ -0,0 +1,104 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Fusion
import Quickshell.Wayland
Rectangle {
id: root
required property LockContext context
readonly property ColorGroup colors: Window.active ? palette.active : palette.inactive
color: colors.window
Button {
text: "Its not working, let me out"
onClicked: context.unlocked();
}
Label {
id: clock
property var date: new Date()
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: 100
}
// The native font renderer tends to look nicer at large sizes.
renderType: Text.NativeRendering
font.pointSize: 80
// updates the clock every second
Timer {
running: true
repeat: true
interval: 1000
onTriggered: clock.date = new Date();
}
// updated when the date changes
text: {
const hours = this.date.getHours().toString().padStart(2, '0');
const minutes = this.date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
}
ColumnLayout {
// Uncommenting this will make the password entry invisible except on the active monitor.
// visible: Window.active
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.verticalCenter
}
RowLayout {
TextField {
id: passwordBox
implicitWidth: 400
padding: 10
focus: true
enabled: !root.context.unlockInProgress
echoMode: TextInput.Password
inputMethodHints: Qt.ImhSensitiveData
// Update the text in the context when the text in the box changes.
onTextChanged: root.context.currentText = this.text;
// Try to unlock when enter is pressed.
onAccepted: root.context.tryUnlock();
// Update the text in the box to match the text in the context.
// This makes sure multiple monitors have the same text.
Connections {
target: root.context
function onCurrentTextChanged() {
passwordBox.text = root.context.currentText;
}
}
}
Button {
text: "Unlock"
padding: 10
// don't steal focus from the text box
focusPolicy: Qt.NoFocus
enabled: !root.context.unlockInProgress && root.context.currentText !== "";
onClicked: root.context.tryUnlock();
}
}
Label {
visible: root.context.showFailure
text: "Incorrect password"
}
}
}

View File

@ -1,52 +0,0 @@
import QtQuick
import QtQuick.Controls.Basic
Item {
required property AuthContext context
Item {
anchors.centerIn: parent
TextField {
id: entryBox
anchors.centerIn: parent
width: 600
font.pointSize: 24
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"
font.pointSize: 24
anchors {
horizontalCenter: entryBox.horizontalCenter
top: entryBox.bottom
topMargin: 40
}
text: {
switch (context.status) {
case AuthContext.Status.FirstLogin: return ""
case AuthContext.Status.Authenticating: return "Authenticating"
case AuthContext.Status.LoginFailed: return "Login Failed"
}
}
}
}
}

View File

@ -1,7 +1,7 @@
# 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.
This is a simple but functional lockscreen that follows the system color scheme.
The only authentication method it supports is a password.
You can run the lockscreen with `quickshell -p shell.qml`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1 @@
auth required pam_unix.so

View File

@ -1,38 +1,31 @@
import QtQuick
import QtQuick.Controls.Basic
import Quickshell
import Quickshell.Wayland
ShellRoot {
AuthContext {
id: authContext
onUnlocked: lock.locked = false
// This stores all the information shared between the lock surfaces on each screen.
LockContext {
id: lockContext
onUnlocked: {
// Unlock the screen before exiting, or the compositor will display a
// fallback lock you can't interact with.
lock.locked = false;
Qt.quit();
}
}
WlSessionLock {
id: lock
// Lock the session immediately when quickshell starts.
locked: true
onLockedChanged: {
if (!locked) Qt.quit();
}
WlSessionLockSurface {
// 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 {
LockSurface {
anchors.fill: parent
context: authContext
context: lockContext
}
}
}
}

View File

@ -2,17 +2,24 @@ import QtQuick
import Quickshell
ShellRoot {
AuthContext {
id: authContext
onUnlocked: Qt.quit()
LockContext {
id: lockContext
onUnlocked: Qt.quit();
}
FloatingWindow {
color: "#303030"
Lockscreen {
LockSurface {
anchors.fill: parent
context: authContext
context: lockContext
}
}
// exit the example if the window closes
Connections {
target: Quickshell
function onLastWindowClosed() {
Qt.quit();
}
}
}