Handle invalid keys coming from Qt
When pressing a key which doesn't exist as Qt.Key, we don't get Qt.Key_unknown like we'd expect, but we get 0x0 instead... Let's add that as a new "nil" key (to not conflict with None/unknown/zero/...) and handle it appropriately. This can be reproduced by doing: setxkbmap -layout us,gr -option grp:alt_shift_toggle and pressing Alt-Shift/Shift-Alt.
This commit is contained in:
parent
52c280ec12
commit
d1854eddaf
@ -145,7 +145,13 @@ class BaseKeyParser(QObject):
|
|||||||
self._count += txt
|
self._count += txt
|
||||||
return QKeySequence.ExactMatch
|
return QKeySequence.ExactMatch
|
||||||
|
|
||||||
sequence = self._sequence.append_event(e)
|
try:
|
||||||
|
sequence = self._sequence.append_event(e)
|
||||||
|
except keyutils.KeyParseError as e:
|
||||||
|
self._debug_log("{} Aborting keychain.".format(e))
|
||||||
|
self.clear_keystring()
|
||||||
|
return QKeySequence.NoMatch
|
||||||
|
|
||||||
match, binding = self._match_key(sequence)
|
match, binding = self._match_key(sequence)
|
||||||
if match == QKeySequence.NoMatch:
|
if match == QKeySequence.NoMatch:
|
||||||
mappings = config.val.bindings.key_mappings
|
mappings = config.val.bindings.key_mappings
|
||||||
|
@ -39,7 +39,7 @@ _MODIFIER_MAP = {
|
|||||||
|
|
||||||
|
|
||||||
def is_printable(key):
|
def is_printable(key):
|
||||||
return key <= 0xff and key != Qt.Key_Space
|
return key <= 0xff and key not in [Qt.Key_Space, 0x0]
|
||||||
|
|
||||||
|
|
||||||
def is_modifier_key(key):
|
def is_modifier_key(key):
|
||||||
@ -126,6 +126,7 @@ def _key_to_string(key):
|
|||||||
special_names[getattr(Qt, 'Key_' + k)] = v
|
special_names[getattr(Qt, 'Key_' + k)] = v
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
special_names[0x0] = 'nil'
|
||||||
|
|
||||||
if key in special_names:
|
if key in special_names:
|
||||||
return special_names[key]
|
return special_names[key]
|
||||||
@ -231,7 +232,7 @@ class KeyInfo:
|
|||||||
modifiers &= ~_MODIFIER_MAP[self.key]
|
modifiers &= ~_MODIFIER_MAP[self.key]
|
||||||
elif is_printable(self.key):
|
elif is_printable(self.key):
|
||||||
# "normal" binding
|
# "normal" binding
|
||||||
if not key_string:
|
if not key_string: # pragma: no cover
|
||||||
raise ValueError("Got empty string for key 0x{:x}!"
|
raise ValueError("Got empty string for key 0x{:x}!"
|
||||||
.format(self.key))
|
.format(self.key))
|
||||||
|
|
||||||
@ -371,6 +372,9 @@ class KeySequence:
|
|||||||
if info.key == Qt.Key_unknown:
|
if info.key == Qt.Key_unknown:
|
||||||
raise KeyParseError(keystr, "Got unknown key!")
|
raise KeyParseError(keystr, "Got unknown key!")
|
||||||
|
|
||||||
|
for seq in self._sequences:
|
||||||
|
assert seq
|
||||||
|
|
||||||
def matches(self, other):
|
def matches(self, other):
|
||||||
"""Check whether the given KeySequence matches with this one.
|
"""Check whether the given KeySequence matches with this one.
|
||||||
|
|
||||||
@ -428,6 +432,9 @@ class KeySequence:
|
|||||||
key = ev.key()
|
key = ev.key()
|
||||||
modifiers = ev.modifiers()
|
modifiers = ev.modifiers()
|
||||||
|
|
||||||
|
if key == 0x0:
|
||||||
|
raise KeyParseError(None, "Got nil key!")
|
||||||
|
|
||||||
if modifiers & Qt.ShiftModifier and key == Qt.Key_Backtab:
|
if modifiers & Qt.ShiftModifier and key == Qt.Key_Backtab:
|
||||||
key = Qt.Key_Tab
|
key = Qt.Key_Tab
|
||||||
|
|
||||||
|
@ -48,7 +48,8 @@ class Key:
|
|||||||
qtest = attr.ib(True)
|
qtest = attr.ib(True)
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
self.member = getattr(Qt, 'Key_' + self.attribute, None)
|
if self.attribute:
|
||||||
|
self.member = getattr(Qt, 'Key_' + self.attribute, None)
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
self.name = self.attribute
|
self.name = self.attribute
|
||||||
|
|
||||||
@ -585,4 +586,6 @@ KEYS = [
|
|||||||
Key('CameraFocus', 'Camera Focus', qtest=False),
|
Key('CameraFocus', 'Camera Focus', qtest=False),
|
||||||
|
|
||||||
Key('unknown', 'Unknown', qtest=False),
|
Key('unknown', 'Unknown', qtest=False),
|
||||||
|
# 0x0 is used by Qt for unknown keys...
|
||||||
|
Key(attribute='', name='nil', member=0x0, qtest=False),
|
||||||
]
|
]
|
||||||
|
@ -182,6 +182,11 @@ class TestHandle:
|
|||||||
keyparser.handle(fake_keyevent(Qt.Key_1), dry_run=True)
|
keyparser.handle(fake_keyevent(Qt.Key_1), dry_run=True)
|
||||||
assert not keyparser._count
|
assert not keyparser._count
|
||||||
|
|
||||||
|
def test_invalid_key(self, fake_keyevent, keyparser):
|
||||||
|
keyparser.handle(fake_keyevent(Qt.Key_B))
|
||||||
|
keyparser.handle(fake_keyevent(0x0))
|
||||||
|
assert not keyparser._sequence
|
||||||
|
|
||||||
def test_valid_keychain(self, handle_text, keyparser):
|
def test_valid_keychain(self, handle_text, keyparser):
|
||||||
# Press 'x' which is ignored because of no match
|
# Press 'x' which is ignored because of no match
|
||||||
handle_text(Qt.Key_X,
|
handle_text(Qt.Key_X,
|
||||||
|
@ -182,7 +182,8 @@ class TestKeySequence:
|
|||||||
with pytest.raises(keyutils.KeyParseError):
|
with pytest.raises(keyutils.KeyParseError):
|
||||||
keyutils.KeySequence(Qt.Key_unknown)
|
keyutils.KeySequence(Qt.Key_unknown)
|
||||||
|
|
||||||
def test_init_invalid(self):
|
@pytest.mark.parametrize('key', [0, -1])
|
||||||
|
def test_init_invalid(self, key):
|
||||||
with pytest.raises(AssertionError):
|
with pytest.raises(AssertionError):
|
||||||
keyutils.KeySequence(-1)
|
keyutils.KeySequence(-1)
|
||||||
|
|
||||||
@ -343,6 +344,13 @@ class TestKeySequence:
|
|||||||
new = seq.append_event(event)
|
new = seq.append_event(event)
|
||||||
assert new == keyutils.KeySequence.parse(expected)
|
assert new == keyutils.KeySequence.parse(expected)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('key', [Qt.Key_unknown, 0x0])
|
||||||
|
def test_append_event_invalid(self, key):
|
||||||
|
seq = keyutils.KeySequence()
|
||||||
|
event = QKeyEvent(QKeyEvent.KeyPress, key, Qt.NoModifier, '')
|
||||||
|
with pytest.raises(keyutils.KeyParseError):
|
||||||
|
seq.append_event(event)
|
||||||
|
|
||||||
@pytest.mark.parametrize('keystr, expected', [
|
@pytest.mark.parametrize('keystr, expected', [
|
||||||
('<Control-x>', keyutils.KeySequence(Qt.ControlModifier | Qt.Key_X)),
|
('<Control-x>', keyutils.KeySequence(Qt.ControlModifier | Qt.Key_X)),
|
||||||
('<Meta-x>', keyutils.KeySequence(Qt.MetaModifier | Qt.Key_X)),
|
('<Meta-x>', keyutils.KeySequence(Qt.MetaModifier | Qt.Key_X)),
|
||||||
@ -405,6 +413,7 @@ def test_key_info_to_event():
|
|||||||
(Qt.Key_Enter, False),
|
(Qt.Key_Enter, False),
|
||||||
(Qt.Key_Space, False),
|
(Qt.Key_Space, False),
|
||||||
(Qt.Key_X | Qt.ControlModifier, False), # Wrong usage
|
(Qt.Key_X | Qt.ControlModifier, False), # Wrong usage
|
||||||
|
(0x0, False), # Used by Qt for unknown keys
|
||||||
|
|
||||||
(Qt.Key_ydiaeresis, True),
|
(Qt.Key_ydiaeresis, True),
|
||||||
(Qt.Key_X, True),
|
(Qt.Key_X, True),
|
||||||
|
Loading…
Reference in New Issue
Block a user