2024-01-20 02:08:06 +00:00
/ *
SPDX - FileCopyrightText: 2014 Aleix Pol Gonzalez < aleixpol @ blue - systems . com >
SPDX - License - Identifier: GPL - 2.0 - or - later
* /
import QtQml 2.15
import QtQuick 2.8
2024-08-09 01:20:25 +00:00
import QtQuick . Controls 2.15
2024-01-20 02:08:06 +00:00
import QtQuick . Layouts 1.1
2024-08-09 01:20:25 +00:00
import Qt5Compat . GraphicalEffects
import QtCore
2024-01-20 02:08:06 +00:00
import org . kde . plasma . core 2.0 as PlasmaCore
2024-08-09 01:20:25 +00:00
import org . kde . plasma . private . keyboardindicator as KeyboardIndicator
2024-01-20 02:08:06 +00:00
import org . kde . plasma . components 3.0 as PlasmaComponents3
import org . kde . plasma . workspace . components 2.0 as PW
2024-08-09 01:20:25 +00:00
import org . kde . kirigami 2.20 as Kirigami
import org . kde . kscreenlocker 1.0 as ScreenLocker
import org . kde . kirigamiaddons . sounds
import QtMultimedia
import org . kde . plasma . plasma5support as Plasma5Support
//import org.kde.breeze.components
2024-01-20 02:08:06 +00:00
import org . kde . plasma . private . sessions 2.0
import "../components"
2024-08-09 01:20:25 +00:00
Item {
2024-01-20 02:08:06 +00:00
id: lockScreenUi
// If we're using software rendering, draw outlines instead of shadows
// See https://bugs.kde.org/show_bug.cgi?id=398317
readonly property bool softwareRendering: GraphicsInfo . api === GraphicsInfo . Software
property bool hadPrompt: false ;
property int currentPage: 0 ;
2024-08-09 01:20:25 +00:00
Kirigami.Theme.colorSet: Kirigami . Theme . Complementary
Kirigami.Theme.inherit: false
//colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
2024-01-20 02:08:06 +00:00
2024-08-09 01:20:25 +00:00
Rectangle {
id: blackRect
anchors.fill: parent
color: "black"
z: 99
opacity: 0
Behavior on opacity {
NumberAnimation { duration: 600 }
}
}
Timer {
id: graceLockTimer
interval: 3000
onTriggered: {
root . clearPassword ( ) ;
authenticator . startAuthenticating ( ) ;
}
}
Timer {
id: successTimer
interval: 800
onTriggered: {
Qt . quit ( ) ;
}
}
2024-01-20 02:08:06 +00:00
function setWrongPasswordScreen ( msg ) {
root . clearPassword ( ) ;
currentMessage . text = msg ;
currentMessageIcon . source = "dialog-error" ;
currentPage = 2 ;
dismissButton . focus = true ;
2024-08-09 01:20:25 +00:00
//graceLockTimer.restart();
}
// This is probably the worst code I've ever written, just so that I can play a themed sound file slightly earlier, on time, instead of letting Plasma decide,
// because Plasma plays the sounds too early/too late for this to be accurate, the biggest offender being the sound that plays when the user successfully logs
// back into the session. Plasma plays it right as kscreenlocker closes, which is too late and sounds jarring as a result.
// It literally executes a kreadconfig to read kdeglobals to extract the sound theme because I cannot for the life of me find the appropriate API calls
// and then it manually *searches* for the appropriate sound file, because the SoundsModel component provided by kirigamiaddons (the only thing I could)
// actually find at all, does not have a standard way of representing these sounds at all.
Plasma5Support . DataSource {
id: executable
engine: "executable"
connectedSources: [ ]
onNewData: ( sourceName , data ) = > {
var stdout = data [ "stdout" ]
exited ( stdout )
disconnectSource ( sourceName ) // cmd finished
}
function exec ( cmd ) {
if ( cmd ) {
connectSource ( cmd )
}
}
signal exited ( string stdout )
2024-01-20 02:08:06 +00:00
}
2024-08-09 01:20:25 +00:00
Connections {
target: executable
function onExited ( stdout ) {
soundsModel . theme = stdout . trim ( ) ? stdout . trim ( ) : "ocean" ;
for ( var i = 0 ; i < soundsModel . rowCount ( ) ; i ++ ) {
var str = soundsModel . initialSourceUrl ( i ) ;
if ( str . includes ( "desktop-login" ) && ! str . endsWith ( ".license" ) ) {
lockSuccess . source = str ;
break ;
}
/ * i f ( s t r . i n c l u d e s ( " d e s k t o p - l o g o u t " ) & & ! s t r . e n d s W i t h ( " . l i c e n s e " ) ) {
lockSound . source = str ;
lockSound . play ( ) ;
} * /
}
}
}
SoundsModel {
id: soundsModel
}
MediaPlayer {
id: lockSuccess
audioOutput: AudioOutput { }
}
/ * M e d i a P l a y e r {
id: lockSound
audioOutput: AudioOutput { }
} * /
2024-01-20 02:08:06 +00:00
Connections {
target: authenticator
2024-08-09 01:20:25 +00:00
function onFailed ( kind ) {
if ( kind != 0 ) { // if this is coming from the noninteractive authenticators
return ;
}
2024-01-20 02:08:06 +00:00
if ( root . notification ) {
root . notification += "\n"
}
setWrongPasswordScreen ( "The user name or password is incorrect." ) ;
lockScreenUi . hadPrompt = false ;
}
function onSucceeded ( ) {
if ( lockScreenUi . hadPrompt ) {
2024-08-09 01:20:25 +00:00
blackRect . opacity = 1 ;
lockSuccess . play ( ) ;
successTimer . start ( ) ;
2024-01-20 02:08:06 +00:00
} else {
currentPage = 4 ;
noPasswordArea . forceActiveFocus ( ) ;
}
}
2024-08-09 01:20:25 +00:00
function onInfoMessageChanged ( ) {
2024-01-20 02:08:06 +00:00
root . clearPassword ( ) ;
2024-08-09 01:20:25 +00:00
currentMessage . text = authenticator . infoMessage ;
2024-01-20 02:08:06 +00:00
currentMessageIcon . source = "dialog-info" ;
currentPage = 2 ;
dismissButton . focus = true ;
}
2024-08-09 01:20:25 +00:00
function onErrorMessageChanged ( ) {
console . log ( "ERROR " + authenticator . errorMessage ) ;
2024-01-20 02:08:06 +00:00
}
2024-08-09 01:20:25 +00:00
function onPromptChanged ( ) {
root . notification = authenticator . prompt ;
2024-01-20 02:08:06 +00:00
passwordArea . mainPasswordBox . forceActiveFocus ( ) ;
lockScreenUi . hadPrompt = true ;
}
2024-08-09 01:20:25 +00:00
function onPromptForSecretChanged ( ) {
2024-01-20 02:08:06 +00:00
passwordArea . mainPasswordBox . forceActiveFocus ( ) ;
lockScreenUi . hadPrompt = true ;
}
}
SessionManagement {
id: sessionManagement
}
Connections {
target: sessionManagement
function onAboutToSuspend ( ) {
root . clearPassword ( ) ;
}
}
SessionsModel {
id: sessionsModel
showNewSessionEntry: false
}
2024-08-09 01:20:25 +00:00
KeyboardIndicator . KeyState {
id: capsLockState
key: Qt . Key_CapsLock
2024-01-20 02:08:06 +00:00
}
Loader {
id: changeSessionComponent
active: false
source: "ChangeSession.qml"
visible: false
}
WallpaperFader {
anchors.fill: parent
source: wallpaper
}
Loader {
id: inputPanel
state: "hidden"
readonly property bool keyboardActive: item ? item.active : false
anchors {
left: parent . left
right: parent . right
bottom: lockScreenUi . bottom
}
function showHide ( ) {
state = state == "hidden" ? "visible" : "hidden" ;
}
Component.onCompleted: {
inputPanel . source = Qt . platform . pluginName . includes ( "wayland" ) ? "../components/VirtualKeyboard_wayland.qml" : "../components/VirtualKeyboard.qml"
}
onKeyboardActiveChanged: {
if ( keyboardActive ) {
inputPanel . z = 99 ;
state = "visible" ;
} else {
state = "hidden" ;
}
}
states: [
State {
name: "visible"
PropertyChanges {
target: lockScreenRoot
height: lockScreenUi . height - inputPanel . height ;
//y: 0 - inputPanel.height + passwordArea.height - (switchuserButton.height + switchuserButton.anchors.topMargin);
}
PropertyChanges {
target: inputPanel
y: lockScreenRoot . height - inputPanel . height
}
} ,
State {
name: "hidden"
PropertyChanges {
target: lockScreenRoot
height: lockScreenUi . height ;
}
PropertyChanges {
target: inputPanel
y: lockScreenRoot . height - lockScreenRoot . height / 4
}
}
]
transitions: [
Transition {
from: "hidden"
to: "visible"
SequentialAnimation {
ScriptAction {
script: {
inputPanel . item . activated = true ;
Qt . inputMethod . show ( ) ;
}
}
ParallelAnimation {
NumberAnimation {
target: lockScreenRoot
property: "height"
2024-08-09 01:20:25 +00:00
duration: Kirigami . Units . longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing . InOutQuad
}
NumberAnimation {
target: inputPanel
property: "y"
2024-08-09 01:20:25 +00:00
duration: Kirigami . Units . longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing . OutQuad
}
}
}
} ,
Transition {
from: "visible"
to: "hidden"
SequentialAnimation {
ParallelAnimation {
NumberAnimation {
target: lockScreenRoot
property: "height"
2024-08-09 01:20:25 +00:00
duration: Kirigami . Units . longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing . InOutQuad
}
NumberAnimation {
target: inputPanel
property: "y"
2024-08-09 01:20:25 +00:00
duration: Kirigami . Units . longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing . InQuad
}
OpacityAnimator {
target: inputPanel
2024-08-09 01:20:25 +00:00
duration: Kirigami . Units . longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing . InQuad
}
}
ScriptAction {
script: {
inputPanel . item . activated = false ;
Qt . inputMethod . hide ( ) ;
}
}
}
}
]
}
MouseArea {
id: lockScreenRoot
property bool calledUnlock: false
Component.onCompleted: {
2024-08-09 01:20:25 +00:00
executable . exec ( "kreadconfig6 --file ~/.config/kdeglobals --group Sounds --key Theme" ) ;
2024-01-20 02:08:06 +00:00
if ( ! calledUnlock ) {
2024-08-09 01:20:25 +00:00
calledUnlock = true ;
authenticator . startAuthenticating ( ) ;
graceLockTimer . restart ( ) ;
2024-01-20 02:08:06 +00:00
}
}
x: parent . x
y: parent . y
width: parent . width
height: parent . height
hoverEnabled: true
drag.filterChildren: true
Keys.onEscapePressed: {
if ( inputPanel . keyboardActive ) {
inputPanel . showHide ( ) ;
}
}
2024-08-09 01:20:25 +00:00
Keys.onPressed: ( event ) = > {
2024-01-20 02:08:06 +00:00
event . accepted = false ;
}
GenericButton {
id: switchLayoutButton
anchors {
top: parent . top
topMargin: 5
left: parent . left
leftMargin: 7
}
implicitWidth: 35
implicitHeight: 28
label.font.pointSize: 9
label.font.capitalization: Font . AllUppercase
focusPolicy: Qt . TabFocus
Accessible.description: i18ndc ( "plasma_lookandfeel_org.kde.lookandfeel" , "Button to change keyboard layout" , "Switch layout" )
PW . KeyboardLayoutSwitcher {
id: keyboardLayoutSwitcher
anchors.fill: parent
acceptedButtons: Qt . NoButton
}
text: keyboardLayoutSwitcher . layoutNames . shortName
onClicked: keyboardLayoutSwitcher . keyboardLayout . switchToNextLayout ( )
visible: keyboardLayoutSwitcher . hasMultipleKeyboardLayouts
}
ListModel {
id: users
Component.onCompleted: {
users . append ( {
name: kscreenlocker_userName ,
realName: kscreenlocker_userName ,
icon: kscreenlocker_userImage ,
} )
}
}
MainBlock {
id: passwordArea
anchors.centerIn: parent
visible: currentPage == 0
focus: true
2024-08-09 01:20:25 +00:00
//enabled: !authenticator.busy
enabled: ! graceLockTimer . running
onPasswordResult: ( password ) = > {
2024-01-20 02:08:06 +00:00
// Switch to the 'Welcome' screen
currentPage = 1 ;
2024-08-09 01:20:25 +00:00
authenticator . startAuthenticating ( ) ;
authenticator . respond ( password ) ;
2024-01-20 02:08:06 +00:00
}
notificationMessage: {
2024-08-09 01:20:25 +00:00
if ( capsLockState . locked ) {
2024-01-20 02:08:06 +00:00
return i18nd ( "plasma_lookandfeel_org.kde.lookandfeel" , "Caps Lock is on" ) ;
} else {
return "" ;
}
}
}
NoPasswordUnlock {
id: noPasswordArea
anchors.horizontalCenter: parent . horizontalCenter
anchors.bottom: switchuserButton . top
anchors.bottomMargin: 52
visible: currentPage == 4
onClicked: {
Qt . quit ( ) ;
}
}
GenericButton {
id: switchuserButton
visible: currentPage == 0 || currentPage == 4
label.font.pointSize: 11
implicitWidth: 108
implicitHeight: 28
focusPolicy: Qt . TabFocus
Accessible.name: i18nd ( "plasma_lookandfeel_org.kde.lookandfeel" , "Switch User" )
PlasmaComponents3 . Label {
font.pointSize: 11
text: i18nd ( "plasma_lookandfeel_org.kde.lookandfeel" , "Switch User" )
anchors.fill: parent
2024-08-09 01:20:25 +00:00
anchors.bottomMargin: Kirigami . Units . smallSpacing / 2
2024-01-20 02:08:06 +00:00
verticalAlignment: Text . AlignVCenter
horizontalAlignment: Text . AlignHCenter
2024-08-09 01:20:25 +00:00
renderType: Text . NativeRendering
font.hintingPreference: Font . PreferFullHinting
font.kerning: false
2024-01-20 02:08:06 +00:00
elide: Text . ElideRight
layer.enabled: true
layer.effect: DropShadow {
//visible: !softwareRendering
horizontalOffset: 0
verticalOffset: 1
radius: 6
samples: 14
spread: 0.0001
color: "#bf000000"
}
}
onClicked: {
2024-08-09 01:20:25 +00:00
sessionManagement . switchUser ( ) ;
//sessionsModel.startNewSession(true /* lock the screen too */)
2024-01-20 02:08:06 +00:00
lockScreenRoot . state = ''
passwordArea . mainPasswordBox . forceActiveFocus ( ) ;
}
anchors {
top: passwordArea . bottom
topMargin: ( currentPage == 4 ? 36 : 40 ) / ( inputPanel . keyboardActive ? 4 : 1 ) // for some reason, Microsoft offset Windows 7's Switch User button a bit when in no password lock
horizontalCenter: parent . horizontalCenter
}
}
RowLayout {
visible: currentPage == 0 || currentPage == 4
id: footer
anchors {
bottom: parent . bottom
left: parent . left
right: parent . right
margins: 34
}
EoAButton {
}
OSKButton {
onClicked: {
// Otherwise the password field loses focus and virtual keyboard
// keystrokes get eaten
passwordArea . mainPasswordBox . forceActiveFocus ( ) ;
inputPanel . showHide ( )
}
visible: inputPanel . status == Loader . Ready
}
Item {
Layout.fillWidth: true
}
}
Item {
id: welcomePage
visible: currentPage == 1
anchors.fill: parent
Status {
id: statusText
anchors.centerIn: parent
anchors.verticalCenterOffset: - 36
statusText: i18nd ( "okular" , "Welcome" )
speen: welcomePage . visible
}
}
ColumnLayout {
id: messagePage
visible: currentPage == 2
anchors {
bottom: switchuserButton . bottom
horizontalCenter: parent . horizontalCenter
}
spacing: 0
RowLayout {
spacing: 10
Kirigami . Icon {
id: currentMessageIcon
implicitHeight: 32
implicitWidth: 32
}
Label {
id: currentMessage
Layout.alignment: Qt . AlignHCenter
font.pointSize: 9
width: implicitWidth
color: "white"
horizontalAlignment: Text . AlignCenter
layer.enabled: true
layer.effect: DropShadow {
//visible: !softwareRendering
horizontalOffset: 0
verticalOffset: 1
radius: 6
samples: 14
spread: 0.0001
color: "#bf000000"
}
}
}
Item {
height: 40
}
GenericButton {
id: dismissButton
Layout.alignment: Qt . AlignHCenter
font.pointSize: 11
implicitWidth: 93
implicitHeight: 28
focusPolicy: Qt . TabFocus
Accessible.name: "OK"
text: "OK"
onClicked: {
2024-08-09 01:20:25 +00:00
authenticator . startAuthenticating ( ) ;
2024-01-20 02:08:06 +00:00
currentPage = 0 ;
passwordArea . mainPasswordBox . forceActiveFocus ( ) ;
}
}
}
RowLayout {
anchors {
bottom: parent . bottom
left: parent . left
right: parent . right
}
height: 96
Rectangle { Layout.fillWidth: true }
Image {
id: watermark
source: "../images/watermark.png"
opacity: ! inputPanel . keyboardActive
}
Rectangle { Layout.fillWidth: true }
}
Loader {
active: true
source: "LockOsd.qml"
anchors {
horizontalCenter: parent . horizontalCenter
top: parent . top
2024-08-09 01:20:25 +00:00
topMargin: Kirigami . Units . largeSpacing
2024-01-20 02:08:06 +00:00
}
}
}
}