diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 0790ea41..e70d25b9 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -444,11 +444,13 @@ Singleton { property int acSuspendTimeout: 0 property int acSuspendBehavior: SettingsData.SuspendBehavior.Suspend property string acProfileName: "" + property int acPostLockMonitorTimeout: 0 property int batteryMonitorTimeout: 0 property int batteryLockTimeout: 0 property int batterySuspendTimeout: 0 property int batterySuspendBehavior: SettingsData.SuspendBehavior.Suspend property string batteryProfileName: "" + property int batteryPostLockMonitorTimeout: 0 property int batteryChargeLimit: 100 property bool lockBeforeSuspend: false property bool loginctlLockIntegration: true diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index a5f03f53..bf5c99f3 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -255,11 +255,13 @@ var SPEC = { acSuspendTimeout: { def: 0 }, acSuspendBehavior: { def: 0 }, acProfileName: { def: "" }, + acPostLockMonitorTimeout: { def: 0 }, batteryMonitorTimeout: { def: 0 }, batteryLockTimeout: { def: 0 }, batterySuspendTimeout: { def: 0 }, batterySuspendBehavior: { def: 0 }, batteryProfileName: { def: "" }, + batteryPostLockMonitorTimeout: { def: 0 }, batteryChargeLimit: { def: 100 }, lockBeforeSuspend: { def: false }, loginctlLockIntegration: { def: true }, @@ -362,7 +364,7 @@ var SPEC = { lockScreenShowMediaPlayer: { def: true }, lockScreenPowerOffMonitorsOnLock: { def: false }, lockAtStartup: { def: false }, - enableFprint: { def: false, onChange: "scheduleAuthApply" }, + enableFprint: { def: false }, maxFprintTries: { def: 15 }, enableU2f: { def: false, onChange: "scheduleAuthApply" }, u2fMode: { def: "or" }, diff --git a/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml b/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml index dc2ea243..4bde60fa 100644 --- a/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml +++ b/quickshell/Modules/BuiltinDesktopPlugins/DesktopClockWidget.qml @@ -58,7 +58,7 @@ Item { } } - property bool enabled: isInstance ? (instanceData?.enabled ?? true) : SettingsData.desktopClockEnabled + enabled: isInstance ? (instanceData?.enabled ?? true) : SettingsData.desktopClockEnabled property real transparency: isInstance ? (cfg.transparency ?? 0.8) : SettingsData.desktopClockTransparency property string colorMode: isInstance ? (cfg.colorMode ?? "primary") : SettingsData.desktopClockColorMode property color customColor: isInstance ? (cfg.customColor ?? "#ffffff") : SettingsData.desktopClockCustomColor diff --git a/quickshell/Modules/BuiltinDesktopPlugins/SystemMonitorWidget.qml b/quickshell/Modules/BuiltinDesktopPlugins/SystemMonitorWidget.qml index 515e74e4..1d21d6bf 100644 --- a/quickshell/Modules/BuiltinDesktopPlugins/SystemMonitorWidget.qml +++ b/quickshell/Modules/BuiltinDesktopPlugins/SystemMonitorWidget.qml @@ -37,7 +37,7 @@ Item { readonly property var cfg: instanceData?.config ?? null readonly property bool isInstance: instanceId !== "" && cfg !== null - property bool enabled: isInstance ? (instanceData?.enabled ?? true) : SettingsData.systemMonitorEnabled + enabled: isInstance ? (instanceData?.enabled ?? true) : SettingsData.systemMonitorEnabled property bool showHeader: isInstance ? (cfg.showHeader ?? true) : SettingsData.systemMonitorShowHeader property real transparency: isInstance ? (cfg.transparency ?? 0.8) : SettingsData.systemMonitorTransparency property string colorMode: isInstance ? (cfg.colorMode ?? "primary") : SettingsData.systemMonitorColorMode diff --git a/quickshell/Modules/ControlCenter/Components/ActionTile.qml b/quickshell/Modules/ControlCenter/Components/ActionTile.qml index d85c779b..fc422e35 100644 --- a/quickshell/Modules/ControlCenter/Components/ActionTile.qml +++ b/quickshell/Modules/ControlCenter/Components/ActionTile.qml @@ -12,7 +12,6 @@ Rectangle { property string text: "" property string secondaryText: "" property bool isActive: false - property bool enabled: true property int widgetIndex: 0 property var widgetData: null property bool editMode: false diff --git a/quickshell/Modules/ControlCenter/Widgets/CompactSlider.qml b/quickshell/Modules/ControlCenter/Widgets/CompactSlider.qml index eeed4c42..f11982b5 100644 --- a/quickshell/Modules/ControlCenter/Widgets/CompactSlider.qml +++ b/quickshell/Modules/ControlCenter/Widgets/CompactSlider.qml @@ -14,7 +14,6 @@ Rectangle { property real value: 0.0 property real maximumValue: 1.0 property real minimumValue: 0.0 - property bool enabled: true signal sliderValueChanged(real value) diff --git a/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml b/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml index 02da286a..6daf7fe7 100644 --- a/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/SmallBatteryButton.qml @@ -10,7 +10,7 @@ Rectangle { LayoutMirroring.childrenInherit: true property bool isActive: BatteryService.batteryAvailable && (BatteryService.isCharging || BatteryService.isPluggedIn) - property bool enabled: BatteryService.batteryAvailable + enabled: BatteryService.batteryAvailable signal clicked diff --git a/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml b/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml index 30c4e367..77d975eb 100644 --- a/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/SmallDiskUsageButton.qml @@ -25,7 +25,7 @@ Rectangle { return parseFloat(selectedMount.percent.replace("%", "")) || 0; } - property bool enabled: DgopService.dgopAvailable + enabled: DgopService.dgopAvailable signal clicked diff --git a/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml b/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml index 7d86947f..7d8c089b 100644 --- a/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/SmallToggleButton.qml @@ -7,7 +7,6 @@ Rectangle { property string iconName: "" property bool isActive: false - property bool enabled: true property real iconRotation: 0 signal clicked diff --git a/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml b/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml index b8adda84..afac6319 100644 --- a/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml +++ b/quickshell/Modules/ControlCenter/Widgets/ToggleButton.qml @@ -11,7 +11,6 @@ Rectangle { property string iconName: "" property string text: "" property bool isActive: false - property bool enabled: true property string secondaryText: "" property real iconRotation: 0 diff --git a/quickshell/Modules/Lock/Lock.qml b/quickshell/Modules/Lock/Lock.qml index 9a5a00fb..1b06c690 100644 --- a/quickshell/Modules/Lock/Lock.qml +++ b/quickshell/Modules/Lock/Lock.qml @@ -147,6 +147,13 @@ Scope { } } + Pam { + id: sharedPam + lockSecured: root.shouldLock + buffer: root.sharedPasswordBuffer + onUnlockRequested: root.unlock() + } + WlSessionLock { id: sessionLock @@ -170,6 +177,7 @@ Scope { anchors.fill: parent visible: lockSurface.isActiveScreen lock: sessionLock + pam: sharedPam sharedPasswordBuffer: root.sharedPasswordBuffer screenName: lockSurface.currentScreenName isLocked: shouldLock diff --git a/quickshell/Modules/Lock/LockScreenContent.qml b/quickshell/Modules/Lock/LockScreenContent.qml index b296e98d..761f5f11 100644 --- a/quickshell/Modules/Lock/LockScreenContent.qml +++ b/quickshell/Modules/Lock/LockScreenContent.qml @@ -23,6 +23,7 @@ Item { property string passwordBuffer: "" property bool demoMode: false + property var pam: demoPam property string screenName: "" property bool unlocking: false property string pamState: "" @@ -58,10 +59,8 @@ Item { return I18n.tr("Too many attempts - locked out"); if (root.pamState === "fail") return I18n.tr("Incorrect password - try again"); - if (pam.fprintState === "error") { - const detail = (pam.fprint.message || "").trim(); - return detail.length > 0 ? I18n.tr("Fingerprint error: %1").arg(detail) : I18n.tr("Fingerprint error"); - } + if (pam.fprintState === "error") + return I18n.tr("Fingerprint error"); if (pam.fprintState === "max") return I18n.tr("Maximum fingerprint attempts reached. Please use password."); if (pam.fprintState === "fail") @@ -745,13 +744,6 @@ Item { easing.type: Theme.standardEasing } } - - Behavior on color { - ColorAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } } } @@ -1639,49 +1631,46 @@ Item { } Pam { - id: pam - lockSecured: !demoMode - onUnlockRequested: { + id: demoPam + lockSecured: false + } + + Connections { + target: root.pam + + function onUnlockRequested() { root.unlocking = true; lockerReadyArmed = false; passwordField.text = ""; root.passwordBuffer = ""; root.unlockRequested(); } - onStateChanged: { - root.pamState = state; - if (state !== "") { - root.unlocking = false; - placeholderDelay.restart(); - passwordField.text = ""; - root.passwordBuffer = ""; - } - } - onU2fPendingChanged: { - if (u2fPending) { - passwordField.text = ""; - root.passwordBuffer = ""; - if (keyboardController.isKeyboardActive) - keyboardController.hide(); - } - } - } - Connections { - target: pam + function onStateChanged() { + root.pamState = root.pam.state; + if (root.pam.state === "") + return; + root.unlocking = false; + placeholderDelay.restart(); + passwordField.text = ""; + root.passwordBuffer = ""; + } + + function onU2fPendingChanged() { + if (!root.pam.u2fPending) + return; + passwordField.text = ""; + root.passwordBuffer = ""; + if (keyboardController.isKeyboardActive) + keyboardController.hide(); + } function onUnlockInProgressChanged() { - if (!pam.unlockInProgress && root.unlocking) + if (!root.pam.unlockInProgress && root.unlocking) root.unlocking = false; } } - Binding { - target: pam - property: "buffer" - value: root.passwordBuffer - } - Timer { id: placeholderDelay diff --git a/quickshell/Modules/Lock/LockSurface.qml b/quickshell/Modules/Lock/LockSurface.qml index 9d1beaeb..91057ce0 100644 --- a/quickshell/Modules/Lock/LockSurface.qml +++ b/quickshell/Modules/Lock/LockSurface.qml @@ -8,6 +8,7 @@ FocusScope { id: root required property WlSessionLock lock + required property var pam required property string sharedPasswordBuffer required property string screenName required property bool isLocked @@ -32,6 +33,7 @@ FocusScope { anchors.fill: parent demoMode: false + pam: root.pam passwordBuffer: root.sharedPasswordBuffer screenName: root.screenName enabled: !videoScreensaver.active diff --git a/quickshell/Modules/Lock/Pam.qml b/quickshell/Modules/Lock/Pam.qml index f5e78591..4ea8b00f 100644 --- a/quickshell/Modules/Lock/Pam.qml +++ b/quickshell/Modules/Lock/Pam.qml @@ -182,6 +182,8 @@ Scope { abort(); return; } + if (active) + return; tries = 0; errorTries = 0; @@ -195,22 +197,23 @@ Scope { if (!available) return; - if (res === PamResult.Success) { + switch (res) { + case PamResult.Success: if (!root.unlockInProgress) { passwd.abort(); root.proceedAfterPrimaryAuth(); } return; - } - - if (res === PamResult.Error) { - root.fprintState = "error"; + case PamResult.Error: errorTries++; - if (errorTries < 5) { + if (errorTries < 200) { abort(); errorRetry.restart(); + return; } - } else if (res === PamResult.MaxTries) { + abort(); + return; + case PamResult.MaxTries: tries++; if (tries < SettingsData.maxFprintTries) { root.fprintState = "fail"; @@ -219,6 +222,9 @@ Scope { root.fprintState = "max"; abort(); } + break; + default: + return; } root.flashMsg(); @@ -297,7 +303,7 @@ Scope { Timer { id: errorRetry - interval: 800 + interval: 1500 onTriggered: fprint.start() } @@ -349,26 +355,22 @@ Scope { id: fprintStateReset interval: 4000 - onTriggered: { - root.fprintState = ""; - fprint.errorTries = 0; - } + onTriggered: root.fprintState = "" } onLockSecuredChanged: { - if (lockSecured) { - SettingsData.refreshAuthAvailability(); - root.state = ""; - root.fprintState = ""; - root.u2fState = ""; - root.u2fPending = false; - root.lockMessage = ""; - root.resetAuthFlows(); - fprint.checkAvail(); - u2f.checkAvail(); - } else { + if (!lockSecured) { root.resetAuthFlows(); + return; } + root.state = ""; + root.fprintState = ""; + root.u2fState = ""; + root.u2fPending = false; + root.lockMessage = ""; + root.resetAuthFlows(); + fprint.checkAvail(); + u2f.checkAvail(); } Connections { diff --git a/quickshell/Modules/Settings/PowerSleepTab.qml b/quickshell/Modules/Settings/PowerSleepTab.qml index 7282d342..d9814947 100644 --- a/quickshell/Modules/Settings/PowerSleepTab.qml +++ b/quickshell/Modules/Settings/PowerSleepTab.qml @@ -7,8 +7,8 @@ import qs.Modules.Settings.Widgets Item { id: root - readonly property var timeoutOptions: [I18n.tr("Never"), I18n.tr("1 minute"), I18n.tr("2 minutes"), I18n.tr("3 minutes"), I18n.tr("5 minutes"), I18n.tr("10 minutes"), I18n.tr("15 minutes"), I18n.tr("20 minutes"), I18n.tr("30 minutes"), I18n.tr("1 hour"), I18n.tr("1 hour 30 minutes"), I18n.tr("2 hours"), I18n.tr("3 hours")] - readonly property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800] + readonly property var timeoutOptions: [I18n.tr("Never"), I18n.tr("15 seconds"), I18n.tr("30 seconds"), I18n.tr("1 minute"), I18n.tr("2 minutes"), I18n.tr("3 minutes"), I18n.tr("5 minutes"), I18n.tr("10 minutes"), I18n.tr("15 minutes"), I18n.tr("20 minutes"), I18n.tr("30 minutes"), I18n.tr("1 hour"), I18n.tr("1 hour 30 minutes"), I18n.tr("2 hours"), I18n.tr("3 hours")] + readonly property var timeoutValues: [0, 15, 30, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800] function getTimeoutIndex(timeout) { var idx = timeoutValues.indexOf(timeout); @@ -260,6 +260,39 @@ Item { } } + SettingsDropdownRow { + id: postLockMonitorDropdown + settingKey: "postLockMonitorTimeout" + tags: ["monitor", "display", "screen", "timeout", "off", "lock", "after", "post"] + text: I18n.tr("Turn off monitors after lock") + options: root.timeoutOptions + + Connections { + target: powerCategory + function onCurrentIndexChanged() { + const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acPostLockMonitorTimeout : SettingsData.batteryPostLockMonitorTimeout; + postLockMonitorDropdown.currentValue = root.timeoutOptions[root.getTimeoutIndex(currentTimeout)]; + } + } + + Component.onCompleted: { + const currentTimeout = powerCategory.currentIndex === 0 ? SettingsData.acPostLockMonitorTimeout : SettingsData.batteryPostLockMonitorTimeout; + currentValue = root.timeoutOptions[root.getTimeoutIndex(currentTimeout)]; + } + + onValueChanged: value => { + const index = root.timeoutOptions.indexOf(value); + if (index < 0) + return; + const timeout = root.timeoutValues[index]; + if (powerCategory.currentIndex === 0) { + SettingsData.set("acPostLockMonitorTimeout", timeout); + } else { + SettingsData.set("batteryPostLockMonitorTimeout", timeout); + } + } + } + SettingsDropdownRow { id: suspendDropdown settingKey: "suspendTimeout" diff --git a/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml b/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml index c36fb7c4..343d5356 100644 --- a/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml +++ b/quickshell/Modules/Settings/Widgets/SettingsToggleCard.qml @@ -17,7 +17,6 @@ StyledRect { property string description: "" property string iconName: "" property bool checked: false - property bool enabled: true default property alias content: expandedContent.children readonly property bool hasContent: expandedContent.children.length > 0 diff --git a/quickshell/Services/IdleService.qml b/quickshell/Services/IdleService.qml index 9965e47c..f0645096 100644 --- a/quickshell/Services/IdleService.qml +++ b/quickshell/Services/IdleService.qml @@ -36,12 +36,16 @@ Singleton { readonly property int lockTimeout: isOnBattery ? SettingsData.batteryLockTimeout : SettingsData.acLockTimeout readonly property int suspendTimeout: isOnBattery ? SettingsData.batterySuspendTimeout : SettingsData.acSuspendTimeout readonly property int suspendBehavior: isOnBattery ? SettingsData.batterySuspendBehavior : SettingsData.acSuspendBehavior + readonly property int postLockMonitorTimeout: isOnBattery ? SettingsData.batteryPostLockMonitorTimeout : SettingsData.acPostLockMonitorTimeout + readonly property bool postLockMonitorActive: isShellLocked && postLockMonitorTimeout > 0 readonly property bool mediaPlaying: MprisController.activePlayer !== null && MprisController.activePlayer.isPlaying onMonitorTimeoutChanged: _rearmIdleMonitors() onLockTimeoutChanged: _rearmIdleMonitors() onSuspendTimeoutChanged: _rearmIdleMonitors() + onPostLockMonitorTimeoutChanged: _rearmIdleMonitors() + onIsShellLockedChanged: _rearmIdleMonitors() function _rearmIdleMonitors() { _enableGate = false; @@ -60,6 +64,7 @@ Singleton { signal requestSuspend property var monitorOffMonitor: null + property var postLockMonitorOffMonitor: null property var lockMonitor: null property var suspendMonitor: null property var lockComponent: null @@ -96,7 +101,7 @@ Singleton { monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor"); monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout > 0 ? root.monitorTimeout : 86400); monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); - monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0); + monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0 && !root.postLockMonitorActive); monitorOffMonitor.isIdleChanged.connect(function () { if (monitorOffMonitor.isIdle) { if (SettingsData.fadeToDpmsEnabled) { @@ -112,6 +117,18 @@ Singleton { } }); + postLockMonitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.PostLockMonitorOffMonitor"); + postLockMonitorOffMonitor.timeout = Qt.binding(() => root.postLockMonitorTimeout > 0 ? root.postLockMonitorTimeout : 86400); + postLockMonitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); + postLockMonitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.postLockMonitorActive); + postLockMonitorOffMonitor.isIdleChanged.connect(function () { + if (postLockMonitorOffMonitor.isIdle) { + root.requestMonitorOff(); + } else { + root.requestMonitorOn(); + } + }); + lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor"); lockMonitor.timeout = Qt.binding(() => root.lockTimeout > 0 ? root.lockTimeout : 86400); lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); diff --git a/quickshell/Widgets/DankActionButton.qml b/quickshell/Widgets/DankActionButton.qml index c23e84b6..126d5a5a 100644 --- a/quickshell/Widgets/DankActionButton.qml +++ b/quickshell/Widgets/DankActionButton.qml @@ -10,7 +10,6 @@ StyledRect { property color iconColor: Theme.surfaceText property color backgroundColor: "transparent" property bool circular: true - property bool enabled: true property int buttonSize: 32 property var tooltipText: null property string tooltipSide: "bottom" diff --git a/quickshell/Widgets/DankButton.qml b/quickshell/Widgets/DankButton.qml index ea8a202b..6de3a8d4 100644 --- a/quickshell/Widgets/DankButton.qml +++ b/quickshell/Widgets/DankButton.qml @@ -8,7 +8,6 @@ Rectangle { property string text: "" property string iconName: "" property int iconSize: Theme.iconSizeSmall - property bool enabled: true property bool hovered: mouseArea.containsMouse property bool pressed: mouseArea.pressed property color backgroundColor: Theme.buttonBg diff --git a/quickshell/Widgets/DankSlider.qml b/quickshell/Widgets/DankSlider.qml index f91a75e3..fa11ae63 100644 --- a/quickshell/Widgets/DankSlider.qml +++ b/quickshell/Widgets/DankSlider.qml @@ -11,7 +11,6 @@ Item { property int step: 1 property string leftIcon: "" property string rightIcon: "" - property bool enabled: true property string unit: "%" property bool showValue: true property bool isDragging: false diff --git a/quickshell/Widgets/DankTextField.qml b/quickshell/Widgets/DankTextField.qml index 456ee2c2..7aa677f9 100644 --- a/quickshell/Widgets/DankTextField.qml +++ b/quickshell/Widgets/DankTextField.qml @@ -25,7 +25,6 @@ StyledRect { property string placeholderText: "" property alias font: textInput.font property alias textColor: textInput.color - property alias enabled: textInput.enabled property alias echoMode: textInput.echoMode property alias validator: textInput.validator property alias maximumLength: textInput.maximumLength diff --git a/quickshell/Widgets/DankToggle.qml b/quickshell/Widgets/DankToggle.qml index 3b19a5a7..76c21caa 100644 --- a/quickshell/Widgets/DankToggle.qml +++ b/quickshell/Widgets/DankToggle.qml @@ -10,7 +10,6 @@ Item { // API property bool checked: false - property bool enabled: true property bool toggling: false property string text: "" property string description: "" diff --git a/quickshell/assets/pam/fprint b/quickshell/assets/pam/fprint index 770a8a4f..a0f41d7c 100644 --- a/quickshell/assets/pam/fprint +++ b/quickshell/assets/pam/fprint @@ -1,3 +1,3 @@ #%PAM-1.0 -auth required pam_fprintd.so max-tries=1 timeout=5 +auth required pam_fprintd.so max-tries=5