greeter: init greeter

This commit is contained in:
kossLAN 2025-06-20 12:16:19 -04:00
parent be06bf0f86
commit fc9ced0bbe
Signed by: kossLAN
SSH key fingerprint: SHA256:bdV0x+wdQHGJ6LgmstH3KV8OpWY+OOFmJcPcB0wQPV8
6 changed files with 126 additions and 37 deletions

View file

@ -0,0 +1,39 @@
import QtQuick
import Quickshell
import Quickshell.Services.Greetd
import "../lockscreen"
Scope {
id: root
signal launch
property LockState state: LockState {
onTryUnlock: {
this.unlockInProgress = true;
// TODO: env var for user
Greetd.createSession("koss");
}
}
Connections {
target: Greetd
function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) {
if (responseRequired) {
Greetd.respond(root.state.currentText);
} // else ignore - only supporting passwords
}
function onAuthFailure() {
root.state.currentText = "";
root.state.failed();
root.state.unlockInProgress = false;
}
function onReadyToLaunch() {
root.state.unlockInProgress = false;
root.launch();
}
}
}

34
shell/greeter/shell.qml Normal file
View file

@ -0,0 +1,34 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Wayland
import Quickshell.Services.Greetd
import "../lockscreen"
ShellRoot {
id: root
GreeterContext {
id: context
onLaunch: {
lock.locked = false;
Greetd.launch(["hyprland"]);
}
}
WlSessionLock {
id: lock
locked: true
WlSessionLockSurface {
LockSurface {
state: context.state
// TODO: env var for wallpaper
wallpaper: "root:resources/wallpapers/wallhaven-96y9qk.jpg"
anchors.fill: parent
}
}
}
}

View file

@ -1,9 +1,11 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import ".."
Singleton { Singleton {
id: root id: root
@ -22,9 +24,15 @@ Singleton {
} }
LockContext { LockContext {
id: passwordContext id: context
onUnlocked: persist.locked = false Connections {
target: context.state
function onUnlocked() {
persist.locked = false;
}
}
} }
WlSessionLock { WlSessionLock {
@ -34,11 +42,13 @@ Singleton {
WlSessionLockSurface { WlSessionLockSurface {
LockSurface { LockSurface {
state: context.state
wallpaper: ShellSettings.settings.wallpaperUrl
anchors.fill: parent anchors.fill: parent
context: passwordContext
} }
} }
} }
function init() {} function init() {
}
} }

View file

@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Services.Pam import Quickshell.Services.Pam
@ -5,22 +7,15 @@ import Quickshell.Services.Pam
Scope { Scope {
id: root id: root
property string currentText: "" property LockState state: LockState {
property bool unlockInProgress: false onTryUnlock: {
property bool showFailure: false if (this.currentText === "")
signal unlocked
signal failed
// Clear the failure text once the user starts typing.
onCurrentTextChanged: showFailure = false
function tryUnlock() {
if (currentText === "")
return; return;
root.unlockInProgress = true; this.unlockInProgress = true;
pam.start(); pam.start();
} }
}
PamContext { PamContext {
id: pam id: pam
@ -34,20 +29,20 @@ Scope {
// pam_unix will ask for a response for the password prompt // pam_unix will ask for a response for the password prompt
onPamMessage: { onPamMessage: {
if (this.responseRequired) { if (this.responseRequired) {
this.respond(root.currentText); this.respond(root.state.currentText);
} }
} }
// pam_unix won't send any important messages so all we need is the completion status. // pam_unix won't send any important messages so all we need is the completion status.
onCompleted: result => { onCompleted: result => {
if (result == PamResult.Success) { if (result == PamResult.Success) {
root.unlocked(); root.state.unlocked();
root.currentText = ""; root.state.currentText = "";
} else { } else {
root.showFailure = true; root.state.showFailure = true;
} }
root.unlockInProgress = false; root.state.unlockInProgress = false;
} }
} }
} }

View file

@ -0,0 +1,12 @@
import Quickshell
Scope {
property string currentText: ""
property bool unlockInProgress: false
property bool showFailure: false
signal unlocked
signal failed
signal tryUnlock
onCurrentTextChanged: showFailure = false
}

View file

@ -4,19 +4,18 @@ import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import ".."
Rectangle { Item {
id: root id: root
color: Window.active ? ShellSettings.colors["surface"] : ShellSettings.colors["surface_dim"] required property LockState state
required property LockContext context required property string wallpaper
Item { Item {
anchors.fill: parent anchors.fill: parent
Image { Image {
id: bgImage id: bgImage
source: ShellSettings.settings.wallpaperUrl source: root.wallpaper
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
anchors.fill: parent anchors.fill: parent
visible: false visible: false
@ -26,7 +25,7 @@ Rectangle {
anchors.fill: bgImage anchors.fill: bgImage
source: bgImage source: bgImage
radius: 80 radius: 80
transparentBorder: true transparentBorder: false
} }
Rectangle { Rectangle {
@ -134,27 +133,27 @@ Rectangle {
// password input, should probably split this out into a seperate comp // password input, should probably split this out into a seperate comp
LoginField { LoginField {
id: passwordBox id: passwordBox
enabled: !root.context.unlockInProgress enabled: !root.state.unlockInProgress
Layout.preferredWidth: 250 Layout.preferredWidth: 250
Layout.preferredHeight: 30 Layout.preferredHeight: 30
Layout.maximumHeight: 30 Layout.maximumHeight: 30
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
onTextChanged: root.context.currentText = this.text onTextChanged: root.state.currentText = this.text
onAccepted: root.context.tryUnlock() onAccepted: root.state.tryUnlock()
Connections { Connections {
target: root.context target: root.state
function onCurrentTextChanged() { function onCurrentTextChanged() {
if (!passwordBox.shaking) { if (!passwordBox.shaking) {
passwordBox.text = root.context.currentText; passwordBox.text = root.state.currentText;
} }
} }
function onShowFailureChanged() { function onShowFailureChanged() {
if (root.context.showFailure && !passwordBox.shaking) { if (root.state.showFailure && !passwordBox.shaking) {
passwordBox.shaking = true; passwordBox.shaking = true;
} }
} }
@ -185,9 +184,9 @@ Rectangle {
// testing button // testing button
Button { Button {
visible: false visible: true
text: "Emergency Unlock" text: "Emergency Unlock"
onClicked: root.context.unlocked() onClicked: root.state.unlocked()
anchors { anchors {
right: parent.right right: parent.right