lockscreen: replace lockscreen example
The new one uses the pam module and looks much nicer.
This commit is contained in:
		
							parent
							
								
									c30a4faf18
								
							
						
					
					
						commit
						a64b477dea
					
				
					 9 changed files with 190 additions and 126 deletions
				
			
		| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue