Merge pull request #7806 from berndb/lock-ignore-autorepeat
Drop key auto-repeat in the lock screen password field
This commit is contained in:
3 files changed
+158
No files matched your search
@@ -60,6 +60,14 @@ Item {
|
|||||||
passwordTextEdited("")
|
passwordTextEdited("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Waking a DPMS-blanked display can stall the compositor for seconds while
|
||||||
|
// the monitor modesets, so the wake key's release arrives late and client-side
|
||||||
|
// key repeat floods the field with that character. A held key has no business
|
||||||
|
// typing a password; only holding Backspace/Delete to clear stays useful.
|
||||||
|
function dropsAutoRepeat(key) {
|
||||||
|
return key !== Qt.Key_Backspace && key !== Qt.Key_Delete
|
||||||
|
}
|
||||||
|
|
||||||
function syncPasswordText() {
|
function syncPasswordText() {
|
||||||
if (passwordInput.text === passwordText) return
|
if (passwordInput.text === passwordText) return
|
||||||
syncingPasswordText = true
|
syncingPasswordText = true
|
||||||
@@ -199,6 +207,10 @@ Item {
|
|||||||
|
|
||||||
Keys.onPressed: function(event) {
|
Keys.onPressed: function(event) {
|
||||||
root.wakeRequested()
|
root.wakeRequested()
|
||||||
|
if (event.isAutoRepeat && root.dropsAutoRepeat(event.key)) {
|
||||||
|
event.accepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
if (event.key === Qt.Key_Escape || (event.modifiers & Qt.ControlModifier && event.key === Qt.Key_U)) {
|
if (event.key === Qt.Key_Escape || (event.modifiers & Qt.ControlModifier && event.key === Qt.Key_U)) {
|
||||||
root.passwordTextEdited("")
|
root.passwordTextEdited("")
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Commons
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property string resultPath: Quickshell.env("OMARCHY_QML_TEST_RESULT")
|
||||||
|
readonly property string rootPath: Quickshell.env("OMARCHY_PATH")
|
||||||
|
property var failures: []
|
||||||
|
|
||||||
|
function fail(message) {
|
||||||
|
failures.push(String(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertTrue(condition, message) {
|
||||||
|
if (!condition) fail(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
function shellQuote(value) {
|
||||||
|
return "'" + String(value).replace(/'/g, "'\\''") + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeResult() {
|
||||||
|
var payload = JSON.stringify({
|
||||||
|
ok: failures.length === 0,
|
||||||
|
failures: failures
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resultPath) {
|
||||||
|
Quickshell.execDetached(["bash", "-lc", "printf '%s' " + shellQuote(payload) + " > " + shellQuote(resultPath)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { id: host; width: 800; height: 600 }
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1
|
||||||
|
running: true
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
try {
|
||||||
|
var component = Qt.createComponent("file://" + root.rootPath + "/shell/plugins/lock/LockView.qml", Component.PreferSynchronous)
|
||||||
|
if (component.status !== Component.Ready) {
|
||||||
|
root.fail("LockView failed to load: " + component.errorString())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = component.createObject(host, { width: 800, height: 600, loadBackground: false })
|
||||||
|
if (!view) {
|
||||||
|
root.fail("LockView failed to instantiate: " + component.errorString())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var dropped = [Qt.Key_A, Qt.Key_Z, Qt.Key_0, Qt.Key_Space, Qt.Key_Return, Qt.Key_Enter, Qt.Key_Escape]
|
||||||
|
for (var i = 0; i < dropped.length; i++) {
|
||||||
|
root.assertTrue(view.dropsAutoRepeat(dropped[i]), "auto-repeat of key " + dropped[i] + " is dropped")
|
||||||
|
}
|
||||||
|
|
||||||
|
var kept = [Qt.Key_Backspace, Qt.Key_Delete]
|
||||||
|
for (var j = 0; j < kept.length; j++) {
|
||||||
|
root.assertTrue(!view.dropsAutoRepeat(kept[j]), "auto-repeat of key " + kept[j] + " still edits the field")
|
||||||
|
}
|
||||||
|
|
||||||
|
view.destroy()
|
||||||
|
} catch (error) {
|
||||||
|
root.fail("lock autorepeat fixture threw: " + error)
|
||||||
|
} finally {
|
||||||
|
root.writeResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
|
TMPDIR=""
|
||||||
|
QS_PID=""
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n $QS_PID ]] && kill -0 "$QS_PID" 2>/dev/null; then
|
||||||
|
kill "$QS_PID" 2>/dev/null || true
|
||||||
|
wait "$QS_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ -n $TMPDIR && -d $TMPDIR ]]; then
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
require_compositor "lock autorepeat test"
|
||||||
|
|
||||||
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
|
pass "quickshell not installed; skipping lock autorepeat test"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_command jq
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
result="$TMPDIR/result.json"
|
||||||
|
log="$TMPDIR/quickshell.log"
|
||||||
|
config_dir="$TMPDIR/lock-autorepeat"
|
||||||
|
mkdir -p "$config_dir" "$TMPDIR/home"
|
||||||
|
cp "$SHELL_TEST_DIR/fixtures/lock-autorepeat/shell.qml" "$config_dir/shell.qml"
|
||||||
|
ln -s "$ROOT/shell/Ui" "$config_dir/Ui"
|
||||||
|
ln -s "$ROOT/shell/Commons" "$config_dir/Commons"
|
||||||
|
|
||||||
|
OMARCHY_PATH="$ROOT" \
|
||||||
|
OMARCHY_QML_TEST_RESULT="$result" \
|
||||||
|
HOME="$TMPDIR/home" \
|
||||||
|
XDG_CONFIG_HOME="$TMPDIR/home/.config" \
|
||||||
|
XDG_CACHE_HOME="$TMPDIR/home/.cache" \
|
||||||
|
XDG_STATE_HOME="$TMPDIR/home/.local/state" \
|
||||||
|
QML2_IMPORT_PATH="$ROOT/shell${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}" \
|
||||||
|
QML_IMPORT_PATH="$ROOT/shell${QML_IMPORT_PATH:+:$QML_IMPORT_PATH}" \
|
||||||
|
PATH="$ROOT/bin:$PATH" \
|
||||||
|
quickshell -p "$config_dir" --no-color >"$log" 2>&1 &
|
||||||
|
QS_PID=$!
|
||||||
|
|
||||||
|
for _ in {1..80}; do
|
||||||
|
[[ -s $result ]] && break
|
||||||
|
if ! kill -0 "$QS_PID" 2>/dev/null; then
|
||||||
|
sed -n '1,220p' "$log" >&2
|
||||||
|
fail "lock autorepeat quickshell exited before writing result"
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -s $result ]] || {
|
||||||
|
sed -n '1,220p' "$log" >&2
|
||||||
|
fail "lock autorepeat test timed out"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! jq -e '.ok == true' "$result" >/dev/null; then
|
||||||
|
printf 'Lock autorepeat result:\n' >&2
|
||||||
|
jq . "$result" >&2
|
||||||
|
printf 'Lock autorepeat log:\n' >&2
|
||||||
|
sed -n '1,220p' "$log" >&2
|
||||||
|
fail "lock password field drops auto-repeat except Backspace/Delete"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pass "lock password field drops auto-repeat except Backspace/Delete"
|
||||||
Reference in new issue
Block a user