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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user