diff --git a/shell/greeter/GreeterContext.qml b/shell/greeter/GreeterContext.qml new file mode 100644 index 0000000..ea35866 --- /dev/null +++ b/shell/greeter/GreeterContext.qml @@ -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(); + } + } +} diff --git a/shell/greeter/shell.qml b/shell/greeter/shell.qml new file mode 100644 index 0000000..04ffcd0 --- /dev/null +++ b/shell/greeter/shell.qml @@ -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 + } + } + } +} diff --git a/shell/lockscreen/Controller.qml b/shell/lockscreen/Controller.qml index b26e7ad..527e9ef 100644 --- a/shell/lockscreen/Controller.qml +++ b/shell/lockscreen/Controller.qml @@ -1,9 +1,11 @@ pragma Singleton pragma ComponentBehavior: Bound +import QtQuick import Quickshell import Quickshell.Io import Quickshell.Wayland +import ".." Singleton { id: root @@ -22,9 +24,15 @@ Singleton { } LockContext { - id: passwordContext + id: context - onUnlocked: persist.locked = false + Connections { + target: context.state + + function onUnlocked() { + persist.locked = false; + } + } } WlSessionLock { @@ -34,11 +42,13 @@ Singleton { WlSessionLockSurface { LockSurface { + state: context.state + wallpaper: ShellSettings.settings.wallpaperUrl anchors.fill: parent - context: passwordContext } } } - function init() {} + function init() { + } } diff --git a/shell/lockscreen/LockContext.qml b/shell/lockscreen/LockContext.qml index fa7b85f..64f4c27 100644 --- a/shell/lockscreen/LockContext.qml +++ b/shell/lockscreen/LockContext.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import QtQuick import Quickshell import Quickshell.Services.Pam @@ -5,21 +7,14 @@ import Quickshell.Services.Pam Scope { id: root - property string currentText: "" - property bool unlockInProgress: false - property bool showFailure: false - signal unlocked - signal failed + property LockState state: LockState { + onTryUnlock: { + if (this.currentText === "") + return; - // Clear the failure text once the user starts typing. - onCurrentTextChanged: showFailure = false - - function tryUnlock() { - if (currentText === "") - return; - - root.unlockInProgress = true; - pam.start(); + this.unlockInProgress = true; + pam.start(); + } } PamContext { @@ -34,20 +29,20 @@ Scope { // pam_unix will ask for a response for the password prompt onPamMessage: { 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. onCompleted: result => { if (result == PamResult.Success) { - root.unlocked(); - root.currentText = ""; + root.state.unlocked(); + root.state.currentText = ""; } else { - root.showFailure = true; + root.state.showFailure = true; } - root.unlockInProgress = false; + root.state.unlockInProgress = false; } } } diff --git a/shell/lockscreen/LockState.qml b/shell/lockscreen/LockState.qml new file mode 100644 index 0000000..f50c105 --- /dev/null +++ b/shell/lockscreen/LockState.qml @@ -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 +} diff --git a/shell/lockscreen/LockSurface.qml b/shell/lockscreen/LockSurface.qml index 9b7d854..bac8ca2 100644 --- a/shell/lockscreen/LockSurface.qml +++ b/shell/lockscreen/LockSurface.qml @@ -4,19 +4,18 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls import Qt5Compat.GraphicalEffects -import ".." -Rectangle { +Item { id: root - color: Window.active ? ShellSettings.colors["surface"] : ShellSettings.colors["surface_dim"] - required property LockContext context + required property LockState state + required property string wallpaper Item { anchors.fill: parent Image { id: bgImage - source: ShellSettings.settings.wallpaperUrl + source: root.wallpaper fillMode: Image.PreserveAspectCrop anchors.fill: parent visible: false @@ -26,7 +25,7 @@ Rectangle { anchors.fill: bgImage source: bgImage radius: 80 - transparentBorder: true + transparentBorder: false } Rectangle { @@ -134,27 +133,27 @@ Rectangle { // password input, should probably split this out into a seperate comp LoginField { id: passwordBox - enabled: !root.context.unlockInProgress + enabled: !root.state.unlockInProgress Layout.preferredWidth: 250 Layout.preferredHeight: 30 Layout.maximumHeight: 30 Layout.alignment: Qt.AlignHCenter - onTextChanged: root.context.currentText = this.text - onAccepted: root.context.tryUnlock() + onTextChanged: root.state.currentText = this.text + onAccepted: root.state.tryUnlock() Connections { - target: root.context + target: root.state function onCurrentTextChanged() { if (!passwordBox.shaking) { - passwordBox.text = root.context.currentText; + passwordBox.text = root.state.currentText; } } function onShowFailureChanged() { - if (root.context.showFailure && !passwordBox.shaking) { + if (root.state.showFailure && !passwordBox.shaking) { passwordBox.shaking = true; } } @@ -185,9 +184,9 @@ Rectangle { // testing button Button { - visible: false + visible: true text: "Emergency Unlock" - onClicked: root.context.unlocked() + onClicked: root.state.unlocked() anchors { right: parent.right