1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-05 13:08:29 -04:00

feat(keybinds): add keybind for PAM U2F on lock screen (#2930)

* feat(keybinds): add keybind for PAM U2F on lock screen

* add: tooltip, settings json & error handling

---------
This commit is contained in:
nozsh
2026-08-04 05:09:53 +03:00
committed by GitHub
parent 365474b0d9
commit 6de5593216
6 changed files with 218 additions and 4 deletions
+58
View File
@@ -271,3 +271,61 @@ function getConflictingBinds(keyCombo, currentAction, allBinds, modKey) {
}
return conflicts;
}
function qtKeyFromName(name) {
var n = (name || "").toUpperCase();
if (n.length === 1 && n >= "A" && n <= "Z")
return Qt.Key_A + (n.charCodeAt(0) - 65);
if (n.length === 1 && n >= "0" && n <= "9")
return Qt.Key_0 + (n.charCodeAt(0) - 48);
if (n.length >= 2 && n[0] === "F") {
var f = parseInt(n.slice(1), 10);
if (f >= 1 && f <= 12)
return Qt.Key_F1 + (f - 1);
}
var named = {
"SPACE": Qt.Key_Space,
"TAB": Qt.Key_Tab,
"RETURN": Qt.Key_Return,
"ENTER": Qt.Key_Enter,
"BACKSPACE": Qt.Key_Backspace,
"DELETE": Qt.Key_Delete,
"HOME": Qt.Key_Home,
"END": Qt.Key_End,
"UP": Qt.Key_Up,
"DOWN": Qt.Key_Down,
"LEFT": Qt.Key_Left,
"RIGHT": Qt.Key_Right
};
return named[n] || 0;
}
function isModifierKey(qk) {
return qk === Qt.Key_Control || qk === Qt.Key_Shift || qk === Qt.Key_Alt || qk === Qt.Key_Meta
|| qk === Qt.Key_NumLock || qk === Qt.Key_CapsLock || qk === Qt.Key_ScrollLock;
}
function eventMatchesCombo(event, combo) {
if (!combo)
return false;
var parts = combo.split("+");
var keyName = parts[parts.length - 1].trim().toUpperCase();
var wantsShift = false;
var hasCtrl = false;
for (var i = 0; i < parts.length - 1; i++) {
var mod = parts[i].trim().toLowerCase();
if (mod === "shift")
wantsShift = true;
else if (mod === "ctrl" || mod === "control")
hasCtrl = true;
else
return false;
}
if (hasCtrl && !(event.modifiers & Qt.ControlModifier))
return false;
if (event.modifiers & (Qt.AltModifier | Qt.MetaModifier))
return false;
if (((event.modifiers & Qt.ShiftModifier) !== 0) !== wantsShift)
return false;
return event.key === qtKeyFromName(keyName);
}
+2
View File
@@ -922,6 +922,8 @@ Singleton {
property bool lockPamInlineU2f: false
property bool lockPamExternallyManaged: false
property string lockU2fPamPath: ""
property string lockScreenSecurityKeyShortcut: "Ctrl+Q"
property bool lockScreenSecurityKeyShortcutEnabled: false
property bool greeterPamExternallyManaged: false
property string lockScreenInactiveColor: "#000000"
property int lockScreenNotificationMode: 0
@@ -473,6 +473,8 @@ var SPEC = {
enableU2f: { def: false, onChange: "scheduleAuthApply" },
u2fMode: { def: "or" },
lockPamPath: { def: "" },
lockScreenSecurityKeyShortcut: { def: "Ctrl+Q" },
lockScreenSecurityKeyShortcutEnabled: { def: false },
lockPamInlineFprint: { def: false },
lockPamInlineU2f: { def: false },
lockPamExternallyManaged: { def: false },
+21 -4
View File
@@ -14,6 +14,7 @@ import qs.Services
import qs.Widgets
import qs.DankCommon.Session
import "../../DankCommon/Common/LayoutCodes.js" as LayoutCodes
import "../../Common/KeyUtils.js" as KeyUtils
Item {
id: root
@@ -98,6 +99,18 @@ Item {
return !demoMode && pam && pam.u2f && pam.u2f.available && SettingsData.enableU2f && SettingsData.u2fMode === "or" && !pam.passwd.active && !pam.u2f.active && !pam.u2fPending && !root.unlocking;
}
function triggerSecurityKeyUnlock() {
if (!canStartSecurityKeyUnlock())
return;
passwordField.clear();
pam.u2f.startForAlternativeAuth();
}
function securityKeyShortcutMatches(event) {
return SettingsData.lockScreenSecurityKeyShortcutEnabled
&& KeyUtils.eventMatchesCombo(event, SettingsData.lockScreenSecurityKeyShortcut);
}
Component.onCompleted: {
WeatherService.addRef();
UserInfoService.getUserInfo();
@@ -962,6 +975,12 @@ Item {
}
if ((event.modifiers & Qt.ControlModifier) && !(event.modifiers & (Qt.AltModifier | Qt.MetaModifier))) {
if (securityKeyShortcutMatches(event) && canStartSecurityKeyUnlock()) {
triggerSecurityKeyUnlock();
event.accepted = true;
return;
}
switch (event.key) {
case Qt.Key_A:
cursorPosition = 0;
@@ -1238,10 +1257,8 @@ Item {
buttonSize: 32
visible: root.canStartSecurityKeyUnlock()
enabled: visible
onClicked: {
passwordField.clear();
pam.u2f.startForAlternativeAuth();
}
tooltipText: SettingsData.lockScreenSecurityKeyShortcutEnabled ? I18n.tr("Security key (%1)", "lock screen security key button tooltip with shortcut").arg(SettingsData.lockScreenSecurityKeyShortcut) : I18n.tr("Security key", "lock screen security key button tooltip")
onClicked: root.triggerSecurityKeyUnlock()
}
DankActionButton {
id: virtualKeyboardButton
@@ -5,6 +5,7 @@ import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
import "../../Common/KeyUtils.js" as KeyUtils
Item {
id: root
@@ -518,6 +519,117 @@ Item {
}
}
SettingsToggleRow {
settingKey: "lockScreenSecurityKeyShortcutEnabled"
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "shortcut", "keybind", "authentication"]
text: I18n.tr("Security key shortcut", "lock screen security key shortcut toggle")
description: I18n.tr("Keyboard shortcut to start security key unlock", "lock screen security key shortcut setting")
checked: SettingsData.lockScreenSecurityKeyShortcutEnabled
visible: SettingsData.enableU2f && SettingsData.u2fMode === "or" && !root.lockU2fControlledByPrimary
onToggled: checked => SettingsData.set("lockScreenSecurityKeyShortcutEnabled", checked)
}
Row {
width: parent.width - Theme.spacingM * 2
x: Theme.spacingM
spacing: Theme.spacingM
visible: SettingsData.lockScreenSecurityKeyShortcutEnabled && SettingsData.enableU2f && SettingsData.u2fMode === "or" && !root.lockU2fControlledByPrimary
Column {
width: parent.width - securityKeyCapture.width - parent.spacing
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Key combination", "lock screen security key shortcut key combination setting")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: securityKeyCapture.captureError !== "" ? securityKeyCapture.captureError : I18n.tr("Press Ctrl+key to set. Esc cancels.", "lock screen security key shortcut key combination capture hint")
font.pixelSize: Theme.fontSizeSmall
color: securityKeyCapture.captureError !== "" ? Theme.warning : Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
horizontalAlignment: Text.AlignLeft
}
}
DankButton {
id: securityKeyCapture
width: 200
anchors.verticalCenter: parent.verticalCenter
focus: capturing
text: capturing ? I18n.tr("Press key...", "lock screen security key shortcut key combination capture prompt") : SettingsData.lockScreenSecurityKeyShortcut
backgroundColor: capturing ? Theme.primaryContainer : Theme.surfaceContainer
textColor: Theme.surfaceText
property bool capturing: false
property string captureError: ""
readonly property var reservedKeys: ["A", "E", "B", "F", "U", "K", "W", "H", "D"]
function startCapture() {
captureError = "";
capturing = true;
securityKeyCapture.forceActiveFocus();
}
function stopCapture() {
capturing = false;
}
onClicked: {
if (capturing)
stopCapture();
else
startCapture();
}
Keys.onPressed: event => {
if (!securityKeyCapture.capturing)
return;
if (KeyUtils.isModifierKey(event.key))
return;
if (event.key === Qt.Key_Escape) {
securityKeyCapture.stopCapture();
event.accepted = true;
return;
}
const mods = KeyUtils.modsFromEvent(event.modifiers);
const hasCtrl = mods.includes("Ctrl");
const hasAlt = mods.includes("Alt") || mods.includes("Super");
if (!hasCtrl || hasAlt)
return;
const hasShift = mods.includes("Shift");
const key = KeyUtils.xkbKeyFromQtKey(event.key, !!(event.modifiers & Qt.KeypadModifier), hasShift);
if (!key)
return;
if (!KeyUtils.qtKeyFromName(key))
return;
if (!hasShift && securityKeyCapture.reservedKeys.indexOf(key.toUpperCase()) !== -1) {
securityKeyCapture.captureError = I18n.tr("Ctrl+%1 is used for password editing", "lock screen security key shortcut reserved key warning").arg(key.toUpperCase());
event.accepted = true;
return;
}
SettingsData.set("lockScreenSecurityKeyShortcut", KeyUtils.formatToken(mods, key));
securityKeyCapture.captureError = "";
securityKeyCapture.stopCapture();
event.accepted = true;
}
}
}
SettingsDropdownRow {
settingKey: "lockU2fPamPath"
tags: ["lock", "screen", "pam", "u2f", "security", "key", "source", "service"]
@@ -5453,6 +5453,29 @@
"yubikey"
]
},
{
"section": "lockScreenSecurityKeyShortcutEnabled",
"label": "Security key shortcut",
"tabIndex": 11,
"category": "Lock Screen",
"keywords": [
"authentication",
"key",
"keybind",
"keyboard",
"lock",
"login",
"password",
"screen",
"security",
"shortcut",
"start",
"u2f",
"unlock",
"yubikey"
],
"description": "Keyboard shortcut to start security key unlock"
},
{
"section": "lockScreenShowMediaPlayer",
"label": "Show Media Player",