mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-02 10:32:07 -04:00
Compare commits
7 Commits
772094eacd
...
e78ba77def
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e78ba77def | ||
|
|
7113afe9e2 | ||
|
|
1a2b6524e6 | ||
|
|
95c4aa9e4c | ||
|
|
9f2518c9e1 | ||
|
|
76c50a654a | ||
|
|
ded2c38551 |
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user