1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-02 10:32:07 -04:00

Compare commits

..

7 Commits

9 changed files with 178 additions and 43 deletions

View File

@@ -86,7 +86,7 @@ touch .qmlls.ini
4. Restart dms to generate the `.qmlls.ini` file 4. Restart dms to generate the `.qmlls.ini` file
5. Run `make lint-qml` from the repo root to lint QML entrypoints (requires the `.qmlls.ini` generated above). The script needs the **Qt 6** `qmllint`; it checks `qmllint6`, `/usr/lib/qt6/bin/qmllint`, then `qmllint` in `PATH`. If your Qt 6 binary lives elsewhere, set `QMLLINT=/path/to/qmllint`. 5. Run `make lint-qml` from the repo root to lint QML entrypoints (requires the `.qmlls.ini` generated above). The script needs the **Qt 6** `qmllint`; it checks `qmllint6`, Fedora's `qmllint-qt6`, `/usr/lib/qt6/bin/qmllint`, then `qmllint` in `PATH`. If your Qt 6 binary lives elsewhere, set `QMLLINT=/path/to/qmllint`.
6. Make your changes, test, and open a pull request. 6. Make your changes, test, and open a pull request.

View File

@@ -1016,13 +1016,20 @@ Singleton {
signal widgetDataChanged signal widgetDataChanged
signal workspaceIconsUpdated signal workspaceIconsUpdated
function refreshAuthAvailability() {
if (isGreeterMode)
return;
Processes.settingsRoot = root;
Processes.detectFprintd();
Processes.detectU2f();
}
Component.onCompleted: { Component.onCompleted: {
if (!isGreeterMode) { if (!isGreeterMode) {
Processes.settingsRoot = root; Processes.settingsRoot = root;
loadSettings(); loadSettings();
initializeListModels(); initializeListModels();
Processes.detectFprintd(); refreshAuthAvailability();
Processes.detectU2f();
Processes.checkPluginSettings(); Processes.checkPluginSettings();
} }
} }
@@ -1262,10 +1269,47 @@ Singleton {
return JSON.stringify(Store.toJson(root), null, 2); return JSON.stringify(Store.toJson(root), null, 2);
} }
function _resetPluginSettings() {
_pluginParseError = false;
pluginSettings = {};
}
function _pluginSettingsErrorCode(error) {
if (typeof error === "number")
return error;
if (error && typeof error === "object") {
if (typeof error.code === "number")
return error.code;
if (typeof error.errno === "number")
return error.errno;
}
const msg = String(error || "").trim();
if (/^\d+$/.test(msg))
return Number(msg);
return -1;
}
function _isMissingPluginSettingsError(error) {
if (_pluginSettingsErrorCode(error) === 2)
return true;
const msg = String(error || "").toLowerCase();
return msg.indexOf("file does not exist") !== -1
|| msg.indexOf("no such file") !== -1
|| msg.indexOf("enoent") !== -1;
}
function loadPluginSettings() { function loadPluginSettings() {
_pluginSettingsLoading = true; try {
parsePluginSettings(pluginSettingsFile.text()); parsePluginSettings(pluginSettingsFile.text());
_pluginSettingsLoading = false; } catch (e) {
const msg = e.message || String(e);
if (!_isMissingPluginSettingsError(e))
console.warn("SettingsData: Failed to load plugin_settings.json. Error:", msg);
_resetPluginSettings();
}
} }
function parsePluginSettings(content) { function parsePluginSettings(content) {
@@ -2701,6 +2745,7 @@ Singleton {
blockLoading: true blockLoading: true
blockWrites: true blockWrites: true
atomicWrites: true atomicWrites: true
printErrors: false
watchChanges: !isGreeterMode watchChanges: !isGreeterMode
onLoaded: { onLoaded: {
if (!isGreeterMode) { if (!isGreeterMode) {
@@ -2709,7 +2754,10 @@ Singleton {
} }
onLoadFailed: error => { onLoadFailed: error => {
if (!isGreeterMode) { if (!isGreeterMode) {
pluginSettings = {}; const msg = String(error || "");
if (!_isMissingPluginSettingsError(error))
console.warn("SettingsData: Failed to load plugin_settings.json. Error:", msg);
_resetPluginSettings();
} }
} }
} }

View File

@@ -10,15 +10,39 @@ Singleton {
property var settingsRoot: null property var settingsRoot: null
function envFlag(name) {
const value = (Quickshell.env(name) || "").trim().toLowerCase();
if (value === "1" || value === "true" || value === "yes" || value === "on")
return true;
if (value === "0" || value === "false" || value === "no" || value === "off")
return false;
return null;
}
readonly property var forcedFprintAvailable: envFlag("DMS_FORCE_FPRINT_AVAILABLE")
readonly property var forcedU2fAvailable: envFlag("DMS_FORCE_U2F_AVAILABLE")
function detectQtTools() { function detectQtTools() {
qtToolsDetectionProcess.running = true; qtToolsDetectionProcess.running = true;
} }
function detectFprintd() { function detectFprintd() {
if (!settingsRoot)
return;
if (forcedFprintAvailable !== null) {
settingsRoot.fprintdAvailable = forcedFprintAvailable;
return;
}
fprintdDetectionProcess.running = true; fprintdDetectionProcess.running = true;
} }
function detectU2f() { function detectU2f() {
if (!settingsRoot)
return;
if (forcedU2fAvailable !== null) {
settingsRoot.u2fAvailable = forcedU2fAvailable;
return;
}
u2fDetectionProcess.running = true; u2fDetectionProcess.running = true;
} }

View File

@@ -363,21 +363,21 @@ Item {
} }
function submitBufferedPassword() { function submitBufferedPassword() {
if (!GreeterState.passwordBuffer || GreeterState.passwordBuffer.length === 0)
return false;
pendingPasswordResponse = false; pendingPasswordResponse = false;
resetPasswordSessionTransition(true); resetPasswordSessionTransition(true);
awaitingExternalAuth = false; awaitingExternalAuth = false;
authTimeout.interval = defaultAuthTimeoutMs; authTimeout.interval = defaultAuthTimeoutMs;
authTimeout.restart(); authTimeout.restart();
Greetd.respond(GreeterState.passwordBuffer); // Some PAM stacks expect an explicit empty response to advance U2F/fprint or fail normally.
Greetd.respond(GreeterState.passwordBuffer || "");
GreeterState.passwordBuffer = ""; GreeterState.passwordBuffer = "";
inputField.text = ""; inputField.text = "";
return true; return true;
} }
function requestPasswordSessionTransition() { function requestPasswordSessionTransition() {
if (!GreeterState.passwordBuffer || GreeterState.passwordBuffer.length === 0) const hasPasswordBuffer = GreeterState.passwordBuffer && GreeterState.passwordBuffer.length > 0;
if (!passwordSubmitRequested && !hasPasswordBuffer)
return; return;
if (cancelingExternalAuthForPassword) if (cancelingExternalAuthForPassword)
return; return;
@@ -402,32 +402,33 @@ Item {
Greetd.cancelSession(); Greetd.cancelSession();
} }
function startAuthSession() { function startAuthSession(submitPassword) {
submitPassword = submitPassword === true;
if (!GreeterState.showPasswordInput || !GreeterState.username) if (!GreeterState.showPasswordInput || !GreeterState.username)
return; return;
if (GreeterState.unlocking) if (GreeterState.unlocking)
return; return;
const hasPasswordBuffer = GreeterState.passwordBuffer && GreeterState.passwordBuffer.length > 0; const hasPasswordBuffer = GreeterState.passwordBuffer && GreeterState.passwordBuffer.length > 0;
if (Greetd.state !== GreetdState.Inactive) { if (Greetd.state !== GreetdState.Inactive) {
if (pendingPasswordResponse && hasPasswordBuffer) if (pendingPasswordResponse && submitPassword)
submitBufferedPassword(); submitBufferedPassword();
else if (hasPasswordBuffer) else if (submitPassword)
passwordSubmitRequested = true; passwordSubmitRequested = true;
return; return;
} }
if (cancelingExternalAuthForPassword) { if (cancelingExternalAuthForPassword) {
if (hasPasswordBuffer) if (submitPassword)
passwordSubmitRequested = true; passwordSubmitRequested = true;
return; return;
} }
if (!hasPasswordBuffer && !root.greeterExternalAuthAvailable) if (!submitPassword && !hasPasswordBuffer && !root.greeterExternalAuthAvailable)
return; return;
pendingPasswordResponse = false; pendingPasswordResponse = false;
passwordSubmitRequested = hasPasswordBuffer; passwordSubmitRequested = submitPassword;
awaitingExternalAuth = !hasPasswordBuffer && root.greeterExternalAuthAvailable; awaitingExternalAuth = !submitPassword && !hasPasswordBuffer && root.greeterExternalAuthAvailable;
// Included PAM stacks (system-auth/common-auth/password-auth) may still run // Included PAM stacks (system-auth/common-auth/password-auth) may still run
// biometric/U2F modules before password even when DMS toggles are off. // biometric/U2F modules before password even when DMS toggles are off.
const waitingOnPamExternalBeforePassword = hasPasswordBuffer && root.greeterPamHasExternalAuth; const waitingOnPamExternalBeforePassword = submitPassword && root.greeterPamHasExternalAuth;
authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs; authTimeout.interval = (awaitingExternalAuth || waitingOnPamExternalBeforePassword) ? externalAuthTimeoutMs : defaultAuthTimeoutMs;
authTimeout.restart(); authTimeout.restart();
Greetd.createSession(GreeterState.username); Greetd.createSession(GreeterState.username);
@@ -448,7 +449,7 @@ Item {
return; return;
externalAuthAutoStartedForUser = GreeterState.username; externalAuthAutoStartedForUser = GreeterState.username;
startAuthSession(); startAuthSession(false);
} }
function isExternalAuthPrompt(message, responseRequired) { function isExternalAuthPrompt(message, responseRequired) {
@@ -838,7 +839,7 @@ Item {
} }
onAccepted: { onAccepted: {
if (GreeterState.showPasswordInput) { if (GreeterState.showPasswordInput) {
root.startAuthSession(); root.startAuthSession(true);
} else { } else {
if (text.trim()) { if (text.trim()) {
root.submitUsername(text); root.submitUsername(text);
@@ -959,7 +960,7 @@ Item {
buttonSize: 32 buttonSize: 32
visible: GreeterState.showPasswordInput && root.greeterExternalAuthAvailable && GreeterState.passwordBuffer.length === 0 && (Greetd.state === GreetdState.Inactive || awaitingExternalAuth || pendingPasswordResponse) && !GreeterState.unlocking visible: GreeterState.showPasswordInput && root.greeterExternalAuthAvailable && GreeterState.passwordBuffer.length === 0 && (Greetd.state === GreetdState.Inactive || awaitingExternalAuth || pendingPasswordResponse) && !GreeterState.unlocking
enabled: visible enabled: visible
onClicked: root.startAuthSession() onClicked: root.startAuthSession(false)
} }
DankActionButton { DankActionButton {
id: virtualKeyboardButton id: virtualKeyboardButton
@@ -992,7 +993,7 @@ Item {
enabled: true enabled: true
onClicked: { onClicked: {
if (GreeterState.showPasswordInput) { if (GreeterState.showPasswordInput) {
root.startAuthSession(); root.startAuthSession(true);
} else { } else {
if (inputField.text.trim()) { if (inputField.text.trim()) {
root.submitUsername(inputField.text); root.submitUsername(inputField.text);
@@ -1613,14 +1614,16 @@ Item {
function onStateChanged() { function onStateChanged() {
if (Greetd.state === GreetdState.Inactive) { if (Greetd.state === GreetdState.Inactive) {
const resumePasswordSubmit = cancelingExternalAuthForPassword && passwordSubmitRequested && GreeterState.passwordBuffer && GreeterState.passwordBuffer.length > 0; const resumePasswordSubmit = cancelingExternalAuthForPassword && passwordSubmitRequested;
awaitingExternalAuth = false; awaitingExternalAuth = false;
pendingPasswordResponse = false; pendingPasswordResponse = false;
cancelingExternalAuthForPassword = false; cancelingExternalAuthForPassword = false;
authTimeout.interval = defaultAuthTimeoutMs; authTimeout.interval = defaultAuthTimeoutMs;
authTimeout.stop(); authTimeout.stop();
if (resumePasswordSubmit) { if (resumePasswordSubmit) {
Qt.callLater(root.startAuthSession); Qt.callLater(function() {
root.startAuthSession(true);
});
return; return;
} }
resetPasswordSessionTransition(true); resetPasswordSessionTransition(true);

View File

@@ -14,6 +14,18 @@ import qs.Modules.Settings.Widgets
Item { Item {
id: root id: root
readonly property bool greeterFprintToggleAvailable: SettingsData.fprintdAvailable || SettingsData.greeterEnableFprint
readonly property bool greeterU2fToggleAvailable: SettingsData.u2fAvailable || SettingsData.greeterEnableU2f
function refreshAuthDetection() {
SettingsData.refreshAuthAvailability();
}
onVisibleChanged: {
if (visible)
refreshAuthDetection();
}
ConfirmModal { ConfirmModal {
id: greeterActionConfirm id: greeterActionConfirm
} }
@@ -154,6 +166,7 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
refreshAuthDetection();
Qt.callLater(enumerateFonts); Qt.callLater(enumerateFonts);
Qt.callLater(checkGreeterInstallState); Qt.callLater(checkGreeterInstallState);
} }
@@ -469,13 +482,16 @@ Item {
tags: ["greeter", "fingerprint", "fprintd", "login", "auth"] tags: ["greeter", "fingerprint", "fprintd", "login", "auth"]
text: I18n.tr("Enable fingerprint at login") text: I18n.tr("Enable fingerprint at login")
description: { description: {
if (!SettingsData.fprintdAvailable) if (!SettingsData.fprintdAvailable) {
if (SettingsData.greeterEnableFprint)
return I18n.tr("Enabled in settings, but fingerprint availability could not yet be confirmed. Re-open after enrolling fingerprints or reconnecting the reader.");
return I18n.tr("Not available — install fprintd and enroll fingerprints."); return I18n.tr("Not available — install fprintd and enroll fingerprints.");
}
return SettingsData.greeterEnableFprint ? I18n.tr("Run Sync to apply. Fingerprint-only login may not unlock GNOME Keyring.") : I18n.tr("Only off for DMS-managed PAM lines. If greetd includes system-auth/common-auth/password-auth with pam_fprintd, fingerprint still stays enabled."); return SettingsData.greeterEnableFprint ? I18n.tr("Run Sync to apply. Fingerprint-only login may not unlock GNOME Keyring.") : I18n.tr("Only off for DMS-managed PAM lines. If greetd includes system-auth/common-auth/password-auth with pam_fprintd, fingerprint still stays enabled.");
} }
descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning
checked: SettingsData.greeterEnableFprint checked: SettingsData.greeterEnableFprint
enabled: SettingsData.fprintdAvailable enabled: root.greeterFprintToggleAvailable
onToggled: checked => SettingsData.set("greeterEnableFprint", checked) onToggled: checked => SettingsData.set("greeterEnableFprint", checked)
} }
@@ -484,13 +500,16 @@ Item {
tags: ["greeter", "u2f", "security", "key", "login", "auth"] tags: ["greeter", "u2f", "security", "key", "login", "auth"]
text: I18n.tr("Enable security key at login") text: I18n.tr("Enable security key at login")
description: { description: {
if (!SettingsData.u2fAvailable) if (!SettingsData.u2fAvailable) {
if (SettingsData.greeterEnableU2f)
return I18n.tr("Enabled in settings, but security key availability could not yet be confirmed. Re-open after enrolling keys or updating pam_u2f.");
return I18n.tr("Not available — install pam_u2f and enroll keys."); return I18n.tr("Not available — install pam_u2f and enroll keys.");
}
return SettingsData.greeterEnableU2f ? I18n.tr("Run Sync to apply.") : I18n.tr("Disabled."); return SettingsData.greeterEnableU2f ? I18n.tr("Run Sync to apply.") : I18n.tr("Disabled.");
} }
descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning
checked: SettingsData.greeterEnableU2f checked: SettingsData.greeterEnableU2f
enabled: SettingsData.u2fAvailable enabled: root.greeterU2fToggleAvailable
onToggled: checked => SettingsData.set("greeterEnableU2f", checked) onToggled: checked => SettingsData.set("greeterEnableU2f", checked)
} }
} }

View File

@@ -9,6 +9,19 @@ import qs.Modules.Settings.Widgets
Item { Item {
id: root id: root
readonly property bool lockFprintToggleAvailable: SettingsData.fprintdAvailable || SettingsData.enableFprint
readonly property bool lockU2fToggleAvailable: SettingsData.u2fAvailable || SettingsData.enableU2f
function refreshAuthDetection() {
SettingsData.refreshAuthAvailability();
}
Component.onCompleted: refreshAuthDetection()
onVisibleChanged: {
if (visible)
refreshAuthDetection();
}
FileBrowserModal { FileBrowserModal {
id: videoBrowserModal id: videoBrowserModal
browserTitle: I18n.tr("Select Video or Folder") browserTitle: I18n.tr("Select Video or Folder")
@@ -172,10 +185,16 @@ Item {
settingKey: "enableFprint" settingKey: "enableFprint"
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"] tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
text: I18n.tr("Enable fingerprint authentication") text: I18n.tr("Enable fingerprint authentication")
description: SettingsData.fprintdAvailable ? I18n.tr("Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)") : I18n.tr("Not enrolled", "fingerprint not detected status") description: {
if (SettingsData.fprintdAvailable)
return I18n.tr("Use fingerprint reader for lock screen authentication (requires enrolled fingerprints)");
if (SettingsData.enableFprint)
return I18n.tr("Enabled in settings, but fingerprint availability could not yet be confirmed. Re-open after enrolling fingerprints or reconnecting the reader.");
return I18n.tr("Not available — install fprintd and enroll fingerprints.");
}
descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning descriptionColor: SettingsData.fprintdAvailable ? Theme.surfaceVariantText : Theme.warning
checked: SettingsData.enableFprint checked: SettingsData.enableFprint
enabled: SettingsData.fprintdAvailable enabled: root.lockFprintToggleAvailable
onToggled: checked => SettingsData.set("enableFprint", checked) onToggled: checked => SettingsData.set("enableFprint", checked)
} }
@@ -183,10 +202,16 @@ Item {
settingKey: "enableU2f" settingKey: "enableU2f"
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"] tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"]
text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen") text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen")
description: SettingsData.u2fAvailable ? I18n.tr("Use a FIDO2/U2F security key (e.g. YubiKey) for lock screen authentication (requires enrolled keys)", "lock screen U2F security key setting") : I18n.tr("Not enrolled", "security key not detected status") description: {
if (SettingsData.u2fAvailable)
return I18n.tr("Use a FIDO2/U2F security key (e.g. YubiKey) for lock screen authentication (requires enrolled keys)", "lock screen U2F security key setting");
if (SettingsData.enableU2f)
return I18n.tr("Enabled in settings, but security key availability could not yet be confirmed. Re-open after enrolling keys or updating pam_u2f.");
return I18n.tr("Not available — install pam_u2f and enroll keys.");
}
descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning descriptionColor: SettingsData.u2fAvailable ? Theme.surfaceVariantText : Theme.warning
checked: SettingsData.enableU2f checked: SettingsData.enableU2f
enabled: SettingsData.u2fAvailable enabled: root.lockU2fToggleAvailable
onToggled: checked => SettingsData.set("enableU2f", checked) onToggled: checked => SettingsData.set("enableU2f", checked)
} }
@@ -195,7 +220,7 @@ Item {
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "mode", "factor", "second"] tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "mode", "factor", "second"]
text: I18n.tr("Security key mode", "lock screen U2F security key mode setting") text: I18n.tr("Security key mode", "lock screen U2F security key mode setting")
description: I18n.tr("'Alternative' lets the key unlock on its own. 'Second factor' requires password or fingerprint first, then the key.", "lock screen U2F security key mode setting") description: I18n.tr("'Alternative' lets the key unlock on its own. 'Second factor' requires password or fingerprint first, then the key.", "lock screen U2F security key mode setting")
visible: SettingsData.u2fAvailable && SettingsData.enableU2f visible: SettingsData.enableU2f
options: [I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method"), I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint")] options: [I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method"), I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint")]
currentValue: SettingsData.u2fMode === "and" ? I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint") : I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method") currentValue: SettingsData.u2fMode === "and" ? I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint") : I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method")
onValueChanged: value => { onValueChanged: value => {

View File

@@ -29,6 +29,7 @@ quickshell -p quickshell/
qmlfmt -t 4 -i 4 -b 250 -w path/to/file.qml qmlfmt -t 4 -i 4 -b 250 -w path/to/file.qml
make lint-qml # Run from repo root; requires quickshell/.qmlls.ini (generated by `qs -p quickshell/`) make lint-qml # Run from repo root; requires quickshell/.qmlls.ini (generated by `qs -p quickshell/`)
# Uses Qt 6 qmllint. Override path with QMLLINT=/path/to/qmllint if needed. # Uses Qt 6 qmllint. Override path with QMLLINT=/path/to/qmllint if needed.
# Auto-detects `qmllint6`, Fedora's `qmllint-qt6`, `/usr/lib/qt6/bin/qmllint`, then `qmllint`.
``` ```
## Components ## Components

View File

@@ -93,9 +93,9 @@ Singleton {
`; `;
monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor"); monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor");
monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0); monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout > 0 ? root.monitorTimeout : 86400);
monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout); monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0);
monitorOffMonitor.isIdleChanged.connect(function () { monitorOffMonitor.isIdleChanged.connect(function () {
if (monitorOffMonitor.isIdle) { if (monitorOffMonitor.isIdle) {
if (SettingsData.fadeToDpmsEnabled) { if (SettingsData.fadeToDpmsEnabled) {
@@ -112,9 +112,9 @@ Singleton {
}); });
lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor"); lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor");
lockMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.lockTimeout > 0); lockMonitor.timeout = Qt.binding(() => root.lockTimeout > 0 ? root.lockTimeout : 86400);
lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
lockMonitor.timeout = Qt.binding(() => root.lockTimeout); lockMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.lockTimeout > 0);
lockMonitor.isIdleChanged.connect(function () { lockMonitor.isIdleChanged.connect(function () {
if (lockMonitor.isIdle) { if (lockMonitor.isIdle) {
if (SettingsData.fadeToLockEnabled) { if (SettingsData.fadeToLockEnabled) {
@@ -130,9 +130,9 @@ Singleton {
}); });
suspendMonitor = Qt.createQmlObject(qmlString, root, "IdleService.SuspendMonitor"); suspendMonitor = Qt.createQmlObject(qmlString, root, "IdleService.SuspendMonitor");
suspendMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.suspendTimeout > 0); suspendMonitor.timeout = Qt.binding(() => root.suspendTimeout > 0 ? root.suspendTimeout : 86400);
suspendMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors); suspendMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
suspendMonitor.timeout = Qt.binding(() => root.suspendTimeout); suspendMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.suspendTimeout > 0);
suspendMonitor.isIdleChanged.connect(function () { suspendMonitor.isIdleChanged.connect(function () {
if (suspendMonitor.isIdle) { if (suspendMonitor.isIdle) {
root.requestSuspend(); root.requestSuspend();

View File

@@ -12,8 +12,8 @@ repo_root="$(
quickshell_dir="${repo_root}/quickshell" quickshell_dir="${repo_root}/quickshell"
qmlls_config="${quickshell_dir}/.qmlls.ini" qmlls_config="${quickshell_dir}/.qmlls.ini"
# Resolve qmllint: honour QMLLINT, then try qmllint6, then the common Qt 6 # Resolve qmllint: honour QMLLINT, then try common Qt 6 binary names and
# install path, and finally bare qmllint. We need the Qt 6 build (>= 6.x) # install paths, and finally bare qmllint. We need the Qt 6 build (>= 6.x)
# because older Qt 5 qmllint doesn't understand --ignore-settings / -W. # because older Qt 5 qmllint doesn't understand --ignore-settings / -W.
resolve_qmllint() { resolve_qmllint() {
if [[ -n "${QMLLINT:-}" ]]; then if [[ -n "${QMLLINT:-}" ]]; then
@@ -21,7 +21,7 @@ resolve_qmllint() {
return return
fi fi
local candidate local candidate
for candidate in qmllint6 /usr/lib/qt6/bin/qmllint qmllint; do for candidate in qmllint6 qmllint-qt6 /usr/lib/qt6/bin/qmllint qmllint; do
if command -v -- "${candidate}" >/dev/null 2>&1; then if command -v -- "${candidate}" >/dev/null 2>&1; then
printf '%s\n' "${candidate}" printf '%s\n' "${candidate}"
return return
@@ -35,6 +35,16 @@ if ! qmllint_bin="$(resolve_qmllint)"; then
exit 127 exit 127
fi fi
print_broken_qmlls_link() {
local target=""
target="$(readlink -- "${qmlls_config}" 2>/dev/null || true)"
printf 'error: %s is a broken symlink. lint-qml requires a live Quickshell tooling VFS.\n' "${qmlls_config}" >&2
if [[ -n "${target}" ]]; then
printf 'Broken target: %s\n' "${target}" >&2
fi
print_vfs_recovery
}
trim_ini_value() { trim_ini_value() {
local value="$1" local value="$1"
value="${value#\"}" value="${value#\"}"
@@ -61,6 +71,11 @@ print_vfs_recovery() {
printf ' qs -p %q\n' "${quickshell_dir}" >&2 printf ' qs -p %q\n' "${quickshell_dir}" >&2
} }
if [[ -L "${qmlls_config}" && ! -e "${qmlls_config}" ]]; then
print_broken_qmlls_link
exit 1
fi
if [[ ! -e "${qmlls_config}" ]]; then if [[ ! -e "${qmlls_config}" ]]; then
printf 'error: %s is missing. lint-qml requires the Quickshell tooling VFS.\n' "${qmlls_config}" >&2 printf 'error: %s is missing. lint-qml requires the Quickshell tooling VFS.\n' "${qmlls_config}" >&2
print_vfs_recovery print_vfs_recovery