lockscreen: replace lockscreen example
The new one uses the pam module and looks much nicer.
This commit is contained in:
parent
c30a4faf18
commit
a64b477dea
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
54
lockscreen/LockContext.qml
Normal file
54
lockscreen/LockContext.qml
Normal 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
104
lockscreen/LockSurface.qml
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Lockscreen
|
# Lockscreen
|
||||||
|
|
||||||
This is a barebones lockscreen with a password input box.
|
This is a simple but functional lockscreen that follows the system color scheme.
|
||||||
Note that you MUST have `pamtester` installed or you won't be able to log in.
|
The only authentication method it supports is a password.
|
||||||
|
|
||||||
You can run the lockscreen with `quickshell -p shell.qml`.
|
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 |
1
lockscreen/pam/password.conf
Normal file
1
lockscreen/pam/password.conf
Normal file
|
@ -0,0 +1 @@
|
||||||
|
auth required pam_unix.so
|
|
@ -1,38 +1,31 @@
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls.Basic
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
|
||||||
ShellRoot {
|
ShellRoot {
|
||||||
AuthContext {
|
// This stores all the information shared between the lock surfaces on each screen.
|
||||||
id: authContext
|
LockContext {
|
||||||
onUnlocked: lock.locked = false
|
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 {
|
WlSessionLock {
|
||||||
id: lock
|
id: lock
|
||||||
|
|
||||||
|
// Lock the session immediately when quickshell starts.
|
||||||
locked: true
|
locked: true
|
||||||
|
|
||||||
onLockedChanged: {
|
|
||||||
if (!locked) Qt.quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
WlSessionLockSurface {
|
WlSessionLockSurface {
|
||||||
// You probably want to replace this with an image.
|
LockSurface {
|
||||||
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
|
anchors.fill: parent
|
||||||
context: authContext
|
context: lockContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,24 @@ import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
|
||||||
ShellRoot {
|
ShellRoot {
|
||||||
AuthContext {
|
LockContext {
|
||||||
id: authContext
|
id: lockContext
|
||||||
onUnlocked: Qt.quit()
|
onUnlocked: Qt.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
FloatingWindow {
|
FloatingWindow {
|
||||||
color: "#303030"
|
LockSurface {
|
||||||
|
|
||||||
Lockscreen {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
context: authContext
|
context: lockContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit the example if the window closes
|
||||||
|
Connections {
|
||||||
|
target: Quickshell
|
||||||
|
|
||||||
|
function onLastWindowClosed() {
|
||||||
|
Qt.quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue