mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 04:09:15 -04:00
feat(Greeter): add auto-login feature for startup settings
- Introduced a new cli flag: `dms greeter sync --autologin-only` and updated UI toggle in Greeter settings
This commit is contained in:
@@ -18,6 +18,7 @@ Singleton {
|
||||
readonly property bool rememberLastUser: GreetdEnv.readBoolOverride(Quickshell.env, ["DMS_GREET_REMEMBER_LAST_USER", "DMS_SAVE_USERNAME"], true)
|
||||
|
||||
property string lastSessionId: ""
|
||||
property string lastSessionExec: ""
|
||||
property string lastSuccessfulUser: ""
|
||||
property bool memoryReady: false
|
||||
property bool isLightMode: false
|
||||
@@ -54,6 +55,7 @@ Singleton {
|
||||
return;
|
||||
const memory = JSON.parse(content);
|
||||
lastSessionId = rememberLastSession ? (memory.lastSessionId || "") : "";
|
||||
lastSessionExec = rememberLastSession ? (memory.lastSessionExec || "") : "";
|
||||
lastSuccessfulUser = rememberLastUser ? (memory.lastSuccessfulUser || "") : "";
|
||||
if (!rememberLastSession || !rememberLastUser)
|
||||
saveMemory();
|
||||
@@ -66,6 +68,8 @@ Singleton {
|
||||
let memory = {};
|
||||
if (rememberLastSession && lastSessionId)
|
||||
memory.lastSessionId = lastSessionId;
|
||||
if (rememberLastSession && lastSessionExec)
|
||||
memory.lastSessionExec = lastSessionExec;
|
||||
if (rememberLastUser && lastSuccessfulUser)
|
||||
memory.lastSuccessfulUser = lastSuccessfulUser;
|
||||
memoryFileView.setText(JSON.stringify(memory, null, 2));
|
||||
@@ -73,13 +77,28 @@ Singleton {
|
||||
|
||||
function setLastSessionId(id) {
|
||||
if (!rememberLastSession) {
|
||||
if (lastSessionId !== "") {
|
||||
if (lastSessionId !== "" || lastSessionExec !== "") {
|
||||
lastSessionId = "";
|
||||
lastSessionExec = "";
|
||||
saveMemory();
|
||||
}
|
||||
return;
|
||||
}
|
||||
lastSessionId = id || "";
|
||||
if (!lastSessionId)
|
||||
lastSessionExec = "";
|
||||
saveMemory();
|
||||
}
|
||||
|
||||
function setLastSessionExec(exec) {
|
||||
if (!rememberLastSession) {
|
||||
if (lastSessionExec !== "") {
|
||||
lastSessionExec = "";
|
||||
saveMemory();
|
||||
}
|
||||
return;
|
||||
}
|
||||
lastSessionExec = exec || "";
|
||||
saveMemory();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ Singleton {
|
||||
property bool lockScreenShowProfileImage: true
|
||||
property bool rememberLastSession: true
|
||||
property bool rememberLastUser: true
|
||||
property bool greeterAutoLogin: false
|
||||
property bool greeterEnableFprint: false
|
||||
property bool greeterEnableU2f: false
|
||||
property string greeterWallpaperPath: ""
|
||||
@@ -132,6 +133,9 @@ Singleton {
|
||||
} else {
|
||||
rememberLastUser = settings.greeterRememberLastUser !== undefined ? settings.greeterRememberLastUser : settings.rememberLastUser !== undefined ? settings.rememberLastUser : true;
|
||||
}
|
||||
if (configBaseDir === root._greeterCacheDir) {
|
||||
greeterAutoLogin = settings.greeterAutoLogin !== undefined ? settings.greeterAutoLogin : false;
|
||||
}
|
||||
greeterEnableFprint = settings.greeterEnableFprint !== undefined ? settings.greeterEnableFprint : false;
|
||||
greeterEnableU2f = settings.greeterEnableU2f !== undefined ? settings.greeterEnableU2f : false;
|
||||
greeterWallpaperPath = settings.greeterWallpaperPath !== undefined ? settings.greeterWallpaperPath : "";
|
||||
|
||||
@@ -57,6 +57,7 @@ Item {
|
||||
property int maxPasswordSessionTransitionRetries: 2
|
||||
property bool fprintdProbeComplete: false
|
||||
property bool fprintdHasDevice: false
|
||||
property bool autoLoginOnSuccess: false
|
||||
// Falls back to PAM-only detection until the fprintd D-Bus probe completes.
|
||||
readonly property bool greeterPamHasFprint: greeterPamStackHasModule("pam_fprintd") && (!fprintdProbeComplete || fprintdHasDevice)
|
||||
readonly property bool greeterPamHasU2f: greeterPamStackHasModule("pam_u2f")
|
||||
@@ -524,6 +525,7 @@ Item {
|
||||
passwordFailureCount = 0;
|
||||
clearAuthFeedback();
|
||||
externalAuthAutoStartedForUser = "";
|
||||
root.autoLoginOnSuccess = false;
|
||||
}
|
||||
root.pickerThemeUsername = user;
|
||||
GreeterState.username = user;
|
||||
@@ -646,6 +648,12 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: greeterAutoLoginPendingProcess
|
||||
command: ["sh", "-c", "mkdir -p $(dirname " + JSON.stringify((Quickshell.env("DMS_GREET_CFG_DIR") || "/var/cache/dms-greeter") + "/.local/state/auto-login-sync-pending") + ") && touch " + JSON.stringify((Quickshell.env("DMS_GREET_CFG_DIR") || "/var/cache/dms-greeter") + "/.local/state/auto-login-sync-pending")]
|
||||
running: false
|
||||
}
|
||||
|
||||
Process {
|
||||
id: hyprlandLayoutProcess
|
||||
running: false
|
||||
@@ -870,110 +878,110 @@ Item {
|
||||
anchors.top: parent.top
|
||||
spacing: 0
|
||||
|
||||
property string fullTimeStr: {
|
||||
const format = GreetdSettings.getEffectiveTimeFormat();
|
||||
return systemClock.date.toLocaleTimeString(I18n.locale(), format);
|
||||
}
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
property string hours: timeParts[0] || ""
|
||||
property string minutes: timeParts[1] || ""
|
||||
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
|
||||
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
|
||||
property string ampm: {
|
||||
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i);
|
||||
return match ? match[0].trim() : "";
|
||||
}
|
||||
property bool hasSeconds: timeParts.length > 2
|
||||
property string fullTimeStr: {
|
||||
const format = GreetdSettings.getEffectiveTimeFormat();
|
||||
return systemClock.date.toLocaleTimeString(I18n.locale(), format);
|
||||
}
|
||||
property var timeParts: fullTimeStr.split(':')
|
||||
property string hours: timeParts[0] || ""
|
||||
property string minutes: timeParts[1] || ""
|
||||
property string secondsWithAmPm: timeParts.length > 2 ? timeParts[2] : ""
|
||||
property string seconds: secondsWithAmPm.replace(/\s*(AM|PM|am|pm)$/i, '')
|
||||
property string ampm: {
|
||||
const match = fullTimeStr.match(/\s*(AM|PM|am|pm)$/i);
|
||||
return match ? match[0].trim() : "";
|
||||
}
|
||||
property bool hasSeconds: timeParts.length > 2
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[1] : clockText.hours.length > 0 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hours.length > 1 ? clockText.hours[1] : clockText.hours.length > 0 ? clockText.hours[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
}
|
||||
StyledText {
|
||||
text: ":"
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 0 ? clockText.minutes[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 0 ? clockText.minutes[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 1 ? clockText.minutes[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.minutes.length > 1 ? clockText.minutes[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.hasSeconds ? ":" : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
StyledText {
|
||||
text: clockText.hasSeconds ? ":" : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 0 ? clockText.seconds[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 0 ? clockText.seconds[0] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 1 ? clockText.seconds[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
StyledText {
|
||||
width: 75
|
||||
text: clockText.hasSeconds && clockText.seconds.length > 1 ? clockText.seconds[1] : ""
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: clockText.hasSeconds
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: 20
|
||||
text: " "
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
StyledText {
|
||||
width: 20
|
||||
text: " "
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: clockText.ampm
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
StyledText {
|
||||
text: clockText.ampm
|
||||
font.pixelSize: 120
|
||||
font.weight: Font.Light
|
||||
color: "white"
|
||||
visible: clockText.ampm !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: dateText
|
||||
@@ -1355,7 +1363,7 @@ Item {
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 38
|
||||
Layout.preferredHeight: root.authFeedbackMessage !== "" ? 38 : 0
|
||||
Layout.topMargin: -Theme.spacingS
|
||||
Layout.bottomMargin: -Theme.spacingS
|
||||
text: root.authFeedbackMessage
|
||||
@@ -1374,48 +1382,150 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 0
|
||||
Layout.preferredWidth: switchUserRow.width + Theme.spacingL * 2
|
||||
Layout.preferredHeight: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainer
|
||||
opacity: GreeterState.showPasswordInput ? 1 : 0
|
||||
enabled: GreeterState.showPasswordInput
|
||||
// Password-screen actions: Switch User + Auto-login toggle as one compact chip row
|
||||
Item {
|
||||
id: passwordActions
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
readonly property bool autoLoginAvailable: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Theme.spacingXS
|
||||
Layout.preferredHeight: visible ? 32 : 0
|
||||
visible: GreeterState.showPasswordInput && !GreeterState.unlocking && (root.multipleUsersAvailable || autoLoginAvailable)
|
||||
|
||||
Row {
|
||||
id: switchUserRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "people"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Rectangle {
|
||||
id: switchUserChip
|
||||
|
||||
visible: root.multipleUsersAvailable
|
||||
height: 32
|
||||
width: switchUserContent.implicitWidth + Theme.spacingM * 2
|
||||
radius: height / 2
|
||||
color: Theme.withAlpha(Theme.surfaceVariant, 0.65)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: (switchUserMouse.containsMouse || switchUserMouse.pressed) ? Theme.surfaceTextHover : "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shorterDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankRipple {
|
||||
id: switchUserRipple
|
||||
cornerRadius: switchUserChip.radius
|
||||
rippleColor: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
Row {
|
||||
id: switchUserContent
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: "people"
|
||||
size: 16
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Switch User")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: switchUserMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: mouse => switchUserRipple.trigger(mouse.x, mouse.y)
|
||||
onClicked: root.returnToUserPicker()
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Switch User")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: autoLoginChip
|
||||
|
||||
StateLayer {
|
||||
stateColor: Theme.primary
|
||||
cornerRadius: parent.radius
|
||||
enabled: !GreeterState.unlocking && GreeterState.showPasswordInput
|
||||
onClicked: root.returnToUserPicker()
|
||||
visible: passwordActions.autoLoginAvailable
|
||||
height: 32
|
||||
width: autoLoginContent.implicitWidth + Theme.spacingM * 2
|
||||
radius: height / 2
|
||||
color: root.autoLoginOnSuccess ? Theme.withAlpha(Theme.primary, 0.85) : Theme.withAlpha(Theme.surfaceVariant, 0.65)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: {
|
||||
if (autoLoginMouse.pressed)
|
||||
return root.autoLoginOnSuccess ? Theme.primaryPressed : Theme.surfaceTextHover;
|
||||
if (autoLoginMouse.containsMouse)
|
||||
return root.autoLoginOnSuccess ? Theme.primaryHover : Theme.surfaceTextHover;
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shorterDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankRipple {
|
||||
id: autoLoginRipple
|
||||
cornerRadius: autoLoginChip.radius
|
||||
rippleColor: root.autoLoginOnSuccess ? Theme.primaryText : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
Row {
|
||||
id: autoLoginContent
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: root.autoLoginOnSuccess ? "check" : "login"
|
||||
size: 16
|
||||
color: root.autoLoginOnSuccess ? Theme.primaryText : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Auto-login")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: root.autoLoginOnSuccess ? Font.Medium : Font.Normal
|
||||
color: root.autoLoginOnSuccess ? Theme.primaryText : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: autoLoginMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.autoLoginOnSuccess = !root.autoLoginOnSuccess
|
||||
onPressed: mouse => autoLoginRipple.trigger(mouse.x, mouse.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1984,7 +2094,8 @@ Item {
|
||||
launchTimeout.restart();
|
||||
if (GreetdSettings.rememberLastSession) {
|
||||
GreetdMemory.setLastSessionId(sessionPath);
|
||||
} else if (GreetdMemory.lastSessionId) {
|
||||
GreetdMemory.setLastSessionExec(sessionCmd);
|
||||
} else if (GreetdMemory.lastSessionId || GreetdMemory.lastSessionExec) {
|
||||
GreetdMemory.setLastSessionId("");
|
||||
}
|
||||
if (GreetdSettings.rememberLastUser) {
|
||||
@@ -1992,6 +2103,8 @@ Item {
|
||||
} else if (GreetdMemory.lastSuccessfulUser) {
|
||||
GreetdMemory.setLastSuccessfulUser("");
|
||||
}
|
||||
if (root.autoLoginOnSuccess)
|
||||
greeterAutoLoginPendingProcess.running = true;
|
||||
pendingLaunchCommand = sessionCmd;
|
||||
pendingLaunchEnv = ["XDG_SESSION_TYPE=wayland"];
|
||||
memoryFlushTimer.restart();
|
||||
|
||||
Reference in New Issue
Block a user