Refine Plasma lock screen authentication retries

This commit is contained in:
ajp_anton
2026-09-04 14:58:34 +00:00
parent 088c42bc14
commit 810c8caf74
5 changed files with 325 additions and 45 deletions
+3 -1
View File
@@ -48,7 +48,9 @@ display is turned off or the system resumes from sleep.
The package applies a narrow patch to Plasma 6.7's lock-screen QML and reapplies The package applies a narrow patch to Plasma 6.7's lock-screen QML and reapplies
it after `plasma-desktop` updates. An unknown future layout is left unchanged it after `plasma-desktop` updates. An unknown future layout is left unchanged
instead of being modified speculatively. Removing the package restores Plasma's instead of being modified speculatively. Removing the package restores Plasma's
original behavior. original behavior. It retries lock-screen authentication after resume and the
automatic-lock grace period, but does not modify `kscreenlocker` authentication
code or `fprintd`.
### Plasma task-group shortcuts ### Plasma task-group shortcuts
+55 -41
View File
@@ -1,6 +1,6 @@
--- a/contents/lockscreen/LockScreenUi.qml --- a/contents/lockscreen/LockScreenUi.qml
+++ b/contents/lockscreen/LockScreenUi.qml +++ b/contents/lockscreen/LockScreenUi.qml
@@ -83,6 +83,15 @@ @@ -83,20 +83,32 @@
} }
} }
@@ -16,7 +16,24 @@
SessionManagement { SessionManagement {
id: sessionManagement id: sessionManagement
} }
@@ -108,7 +117,6 @@
KeyboardIndicator.KeyState {
id: capsLockState
key: Qt.Key_CapsLock
}
Connections {
target: sessionManagement
function onAboutToSuspend() {
root.clearPassword();
}
+ function onResumingFromSuspend() {
+ resumeAuthenticationTimer.restart();
+ }
}
RejectPasswordAnimation {
@@ -108,7 +120,6 @@
id: lockScreenRoot id: lockScreenRoot
property bool uiVisible: false property bool uiVisible: false
@@ -24,8 +41,12 @@
property bool blockUI: containsMouse && (mainStack.depth > 1 || mainBlock.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive) property bool blockUI: containsMouse && (mainStack.depth > 1 || mainBlock.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive)
x: parent.x x: parent.x
@@ -119,10 +127,7 @@ @@ -118,13 +129,10 @@
cursorShape: uiVisible ? Qt.ArrowCursor : Qt.BlankCursor width: parent.width
height: parent.height
hoverEnabled: true
- cursorShape: uiVisible ? Qt.ArrowCursor : Qt.BlankCursor
+ cursorShape: Qt.ArrowCursor
drag.filterChildren: true drag.filterChildren: true
onPressed: uiVisible = true; onPressed: uiVisible = true;
- onPositionChanged: { - onPositionChanged: {
@@ -36,49 +57,42 @@
onUiVisibleChanged: { onUiVisibleChanged: {
if (uiVisible) { if (uiVisible) {
Window.window.requestActivate(); Window.window.requestActivate();
@@ -135,6 +140,13 @@ @@ -135,6 +143,18 @@
} }
authenticator.startAuthenticating(); authenticator.startAuthenticating();
} }
+ Timer { + Timer {
+ // Keep the authentication backend active while the prompt is visible. + id: resumeAuthenticationTimer
+ interval: 1000 + interval: 1500
+ running: parent.uiVisible + onTriggered: authenticator.startAuthenticating()
+ repeat: true + }
+ Timer {
+ id: postGraceAuthenticationTimer
+ // Retry once after Plasma's default five-second lock grace period.
+ interval: 6000
+ running: root.viewVisible
+ onTriggered: authenticator.startAuthenticating() + onTriggered: authenticator.startAuthenticating()
+ } + }
onBlockUIChanged: { onBlockUIChanged: {
if (blockUI) { if (blockUI) {
fadeoutTimer.running = false; fadeoutTimer.running = false;
@@ -143,19 +155,12 @@ @@ -194,6 +214,6 @@
fadeoutTimer.restart(); WallpaperFader {
} anchors.fill: parent
} - state: lockScreenRoot.uiVisible ? "on" : "off"
- onExited: { + state: "on"
- uiVisible = false; source: wallpaper
- } mainStack: mainStack
Keys.onEscapePressed: { footer: footer
- // If the escape key is pressed, kscreenlocker will turn off the screen. @@ -211,7 +231,7 @@
- // We do not want to show the password prompt in this case. samples: 15
- if (uiVisible) { spread: 0.2
- uiVisible = false; color : Qt.rgba(0, 0, 0, 0.7)
- if (inputPanel.keyboardActive) { - opacity: lockScreenRoot.uiVisible ? 0 : 1
- inputPanel.showHide(); + opacity: 0
- } Behavior on opacity {
- root.clearPassword(); OpacityAnimator {
+ // KScreenLocker will turn off the screen; leave the prompt ready for wake-up. duration: Kirigami.Units.veryLongDuration * 2
+ if (inputPanel.keyboardActive) { @@ -265 +285 @@
+ inputPanel.showHide(); - lockScreenUiVisible: lockScreenRoot.uiVisible
} + lockScreenUiVisible: true
+ root.clearPassword();
}
Keys.onPressed: event => {
uiVisible = true;
@@ -167,7 +172,6 @@
onTriggered: {
if (!lockScreenRoot.blockUI) {
mainBlock.mainPasswordBox.showPassword = false;
- lockScreenRoot.uiVisible = false;
}
}
}
@@ -1,6 +1,6 @@
Name: plasma-always-show-unlock Name: plasma-always-show-unlock
Version: 0.1.0 Version: 0.1.0
Release: 1%{?dist} Release: 8%{?dist}
Summary: Always show the Plasma lock-screen unlock prompt Summary: Always show the Plasma lock-screen unlock prompt
License: MIT AND GPL-2.0-or-later License: MIT AND GPL-2.0-or-later
@@ -56,5 +56,9 @@ fi
%{_datadir}/plasma-always-show-unlock/LockScreenUi.patch %{_datadir}/plasma-always-show-unlock/LockScreenUi.patch
%changelog %changelog
* Thu Sep 03 2026 fedora-tools contributors - 0.1.0-8
- Keep the prompt visible without continuously restarting authentication
- Retry authentication after resume and the automatic-lock grace period
* Wed Sep 02 2026 fedora-tools contributors - 0.1.0-1 * Wed Sep 02 2026 fedora-tools contributors - 0.1.0-1
- Initial package - Initial package
@@ -176,3 +176,252 @@ Item {
interval: 3000 interval: 3000
onTriggered: root.notification = "" onTriggered: root.notification = ""
} }
Timer {
id: graceLockTimer
interval: 3000
onTriggered: {
root.clearPassword();
authenticator.startAuthenticating();
}
}
PropertyAnimation {
id: launchAnimation
target: lockScreenRoot
property: "opacity"
from: 0
to: 1
duration: Kirigami.Units.veryLongDuration * 2
}
Component.onCompleted: launchAnimation.start();
WallpaperFader {
anchors.fill: parent
state: lockScreenRoot.uiVisible ? "on" : "off"
source: wallpaper
mainStack: mainStack
footer: footer
clock: clock
alwaysShowClock: config.alwaysShowClock && !config.hideClockWhenIdle
}
DropShadow {
id: clockShadow
anchors.fill: clock
source: clock
visible: !lockScreenUi.softwareRendering && config.alwaysShowClock
radius: 7
verticalOffset: 0.8
samples: 15
spread: 0.2
color : Qt.rgba(0, 0, 0, 0.7)
opacity: lockScreenRoot.uiVisible ? 0 : 1
Behavior on opacity {
OpacityAnimator {
duration: Kirigami.Units.veryLongDuration * 2
easing.type: Easing.InOutQuad
}
}
}
Clock {
id: clock
property Item shadow: clockShadow
visible: y > 0 && config.alwaysShowClock
anchors.horizontalCenter: parent.horizontalCenter
y: (mainBlock.userList.y + mainStack.y)/2 - height/2
Layout.alignment: Qt.AlignBaseline
}
ListModel {
id: users
Component.onCompleted: {
users.append({
name: kscreenlocker_userName,
realName: kscreenlocker_userName,
icon: kscreenlocker_userImage !== ""
? "file://" + kscreenlocker_userImage.split("/").map(encodeURIComponent).join("/")
: "",
})
}
}
StackView {
id: mainStack
anchors {
left: parent.left
right: parent.right
}
height: lockScreenRoot.height + Kirigami.Units.gridUnit * 3
focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it
// this isn't implicit, otherwise items still get processed for the scenegraph
visible: opacity > 0
initialItem: MainBlock {
id: mainBlock
lockScreenUiVisible: lockScreenRoot.uiVisible
showUserList: userList.y + mainStack.y > 0
enabled: !graceLockTimer.running
StackView.onStatusChanged: {
// prepare for presenting again to the user
if (StackView.status === StackView.Activating) {
mainPasswordBox.clear();
mainPasswordBox.focus = true;
root.notification = "";
}
}
userListModel: users
notificationMessage: {
const parts = [];
if (capsLockState.locked) {
parts.push(i18ndc("plasma_shell_org.kde.plasma.desktop", "@info:status", "Caps Lock is on"));
}
if (root.notification) {
parts.push(root.notification);
}
return parts.join(" • ");
}
onPasswordResult: password => {
authenticator.respond(password)
}
actionItems: [
ActionButton {
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Slee&p")
icon.name: "system-suspend"
onClicked: sessionManagement.suspend()
visible: sessionManagement.canSuspend
},
ActionButton {
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "&Hibernate")
icon.name: "system-suspend-hibernate"
onClicked: sessionManagement.hibernate()
visible: sessionManagement.canHibernate
},
ActionButton {
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "@action:button", "Switch &User")
icon.name: "system-switch-user"
onClicked: {
sessionManagement.switchUser();
}
visible: sessionManagement.canSwitchUser
}
]
Loader {
Layout.topMargin: Kirigami.Units.smallSpacing // some distance to the password field
Layout.fillWidth: true
Layout.preferredHeight: item ? item.implicitHeight : 0
active: config.showMediaControls
source: "MediaControls.qml"
}
}
}
VirtualKeyboardLoader {
id: inputPanel
z: 1
screenRoot: lockScreenRoot
mainStack: mainStack
mainBlock: mainBlock
passwordField: mainBlock.mainPasswordBox
}
Loader {
z: 2
active: root.viewVisible
source: "LockOsd.qml"
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: Kirigami.Units.gridUnit
}
}
// Note: Containment masks stretch clickable area of their buttons to
// the screen edges, essentially making them adhere to Fitts's law.
// Due to virtual keyboard button having an icon, buttons may have
// different heights, so fillHeight is required.
//
// Note for contributors: Keep this in sync with SDDM Main.qml footer.
RowLayout {
id: footer
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
margins: Kirigami.Units.smallSpacing
}
spacing: Kirigami.Units.smallSpacing
PlasmaComponents3.ToolButton {
id: virtualKeyboardButton
focusPolicy: Qt.TabFocus
text: i18ndc("plasma_shell_org.kde.plasma.desktop", "Button to show/hide virtual keyboard", "Virtual Keyboard")
icon.name: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off"
onClicked: {
// Otherwise the password field loses focus and virtual keyboard
// keystrokes get eaten
mainBlock.mainPasswordBox.forceActiveFocus();
inputPanel.showHide()
}
visible: inputPanel.status === Loader.Ready
Layout.fillHeight: true
containmentMask: Item {
parent: virtualKeyboardButton
anchors.fill: parent
anchors.leftMargin: -footer.anchors.margins
anchors.bottomMargin: -footer.anchors.margins
}
}
PlasmaComponents3.ToolButton {
id: keyboardButton
focusPolicy: Qt.TabFocus
Accessible.description: i18ndc("plasma_shell_org.kde.plasma.desktop", "Button to change keyboard layout", "Switch layout")
icon.name: "input-keyboard"
PW.KeyboardLayoutSwitcher {
id: keyboardLayoutSwitcher
anchors.fill: parent
acceptedButtons: Qt.NoButton
}
text: keyboardLayoutSwitcher.layoutNames.longName
onClicked: keyboardLayoutSwitcher.keyboardLayout.switchToNextLayout()
visible: keyboardLayoutSwitcher.hasMultipleKeyboardLayouts
Layout.fillHeight: true
containmentMask: Item {
parent: keyboardButton
anchors.fill: parent
anchors.leftMargin: virtualKeyboardButton.visible ? 0 : -footer.anchors.margins
anchors.bottomMargin: -footer.anchors.margins
}
}
Item {
Layout.fillWidth: true
}
Battery {}
}
}
}
@@ -35,9 +35,20 @@ fi
run_tool apply >/dev/null run_tool apply >/dev/null
run_tool status >/dev/null run_tool status >/dev/null
grep -q 'function onViewVisibleChanged()' "$target" grep -q 'function onViewVisibleChanged()' "$target"
grep -q 'running: parent.uiVisible' "$target" grep -q 'id: resumeAuthenticationTimer' "$target"
grep -q 'id: postGraceAuthenticationTimer' "$target"
grep -q 'interval: 1500' "$target"
grep -q 'interval: 6000' "$target"
grep -q 'running: root.viewVisible' "$target"
grep -q 'onTriggered: authenticator.startAuthenticating()' "$target" grep -q 'onTriggered: authenticator.startAuthenticating()' "$target"
if grep -q 'seenPositionChange\|lockScreenRoot.uiVisible = false' "$target"; then grep -q 'function onResumingFromSuspend()' "$target"
[[ $(grep -c 'resumeAuthenticationTimer.restart()' "$target") -eq 1 ]]
grep -q 'cursorShape: Qt.ArrowCursor' "$target"
grep -q 'state: "on"' "$target"
grep -q 'opacity: 0' "$target"
grep -q 'lockScreenUiVisible: true' "$target"
grep -q 'lockScreenRoot.uiVisible = false' "$target"
if grep -q 'seenPositionChange\|running: parent.uiVisible\|repeat: true' "$target"; then
printf 'The patched fixture still contains prompt-hiding logic.\n' >&2 printf 'The patched fixture still contains prompt-hiding logic.\n' >&2
exit 1 exit 1
fi fi