From a8e4d7bb2aceeeca607458e4c846fa4bdf97f55f Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 14 May 2026 20:10:11 -0400 Subject: [PATCH] Forward keypress bug fixed. --- src/fenrirscreenreader/fenrirVersion.py | 2 +- .../inputDriver/x11Driver.py | 9 ++-- .../screenDriver/ptyDriver.py | 23 +++++++++- tests/unit/test_pty_terminal_sequences.py | 43 +++++++++++++++++++ tests/unit/test_x11_terminal_mode.py | 19 ++++++++ 5 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index 7f2ac83e..1e6dd448 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. -version = "2026.05.13" +version = "2026.05.14" code_name = "testing" diff --git a/src/fenrirscreenreader/inputDriver/x11Driver.py b/src/fenrirscreenreader/inputDriver/x11Driver.py index 04ae44fb..ec78a434 100644 --- a/src/fenrirscreenreader/inputDriver/x11Driver.py +++ b/src/fenrirscreenreader/inputDriver/x11Driver.py @@ -426,10 +426,13 @@ class driver(inputDriver): return names def keysym_to_name(self, keysym): - keysym_name = XK.keysym_to_string(keysym) - if keysym_name: + keysym_name = KEYSYM_NAME_MAP.get(keysym) + if keysym_name and keysym >= 0xFF00: return keysym_name - return KEYSYM_NAME_MAP.get(keysym) + keysym_string = XK.keysym_to_string(keysym) + if keysym_string: + return keysym_string + return keysym_name def keysym_name_to_key_name(self, keysym_name): if not keysym_name: diff --git a/src/fenrirscreenreader/screenDriver/ptyDriver.py b/src/fenrirscreenreader/screenDriver/ptyDriver.py index af1fe2e1..1b0c5504 100644 --- a/src/fenrirscreenreader/screenDriver/ptyDriver.py +++ b/src/fenrirscreenreader/screenDriver/ptyDriver.py @@ -351,7 +351,7 @@ class driver(screenDriver): ) def synthesize_backspace_shortcut(self, msg_bytes, event_queue): - if msg_bytes not in [b"\x7f", b"\x08"]: + if not self.is_backspace_shortcut_sequence(msg_bytes): return False if "KEY_FENRIR" not in self.env["input"]["curr_input"]: return False @@ -380,6 +380,27 @@ class driver(screenDriver): ) return True + def is_backspace_shortcut_sequence(self, msg_bytes): + if msg_bytes in [b"\x7f", b"\x08"]: + return True + try: + sequence = msg_bytes.decode("ascii") + except UnicodeDecodeError: + return False + if not sequence.startswith("\x1b["): + return False + if sequence.endswith("~"): + parts = sequence[2:-1].split(";") + if parts[0] == "3": + return True + if parts[0] == "27" and parts[-1] in ["8", "127"]: + return True + elif sequence.endswith("u"): + parts = sequence[2:-1].split(";") + if parts[0] in ["8", "127"]: + return True + return False + def get_session_information(self): self.env["screen"]["autoIgnoreScreens"] = [] self.env["general"]["prev_user"] = getpass.getuser() diff --git a/tests/unit/test_pty_terminal_sequences.py b/tests/unit/test_pty_terminal_sequences.py index 5ddcd2b1..8a2583d1 100644 --- a/tests/unit/test_pty_terminal_sequences.py +++ b/tests/unit/test_pty_terminal_sequences.py @@ -217,6 +217,35 @@ def test_pty_backspace_with_fenrir_key_synthesizes_shortcut_events(): assert second_event["data"]["event_state"] == 0 +@pytest.mark.unit +@pytest.mark.parametrize( + "sequence", + [ + b"\x08", + b"\x1b[3~", + b"\x1b[3;5~", + b"\x1b[27;5;8~", + b"\x1b[27;5;127~", + b"\x1b[8;5u", + b"\x1b[127;5u", + ], +) +def test_pty_xterm_backspace_variants_with_fenrir_key_synthesize_shortcut_events( + sequence, +): + pty_driver = PtyDriver() + event_queue = Mock() + pty_driver.env = { + "input": {"curr_input": ["KEY_FENRIR"]}, + } + + handled = pty_driver.synthesize_backspace_shortcut(sequence, event_queue) + + assert handled is True + first_event = event_queue.put.call_args_list[0].args[0] + assert first_event["data"]["event_name"] == "KEY_BACKSPACE" + + @pytest.mark.unit def test_pty_plain_backspace_is_not_synthesized(): pty_driver = PtyDriver() @@ -229,3 +258,17 @@ def test_pty_plain_backspace_is_not_synthesized(): assert handled is False event_queue.put.assert_not_called() + + +@pytest.mark.unit +def test_pty_plain_delete_sequence_is_not_synthesized(): + pty_driver = PtyDriver() + event_queue = Mock() + pty_driver.env = { + "input": {"curr_input": []}, + } + + handled = pty_driver.synthesize_backspace_shortcut(b"\x1b[3~", event_queue) + + assert handled is False + event_queue.put.assert_not_called() diff --git a/tests/unit/test_x11_terminal_mode.py b/tests/unit/test_x11_terminal_mode.py index 79ed7ae4..c9fed7c2 100644 --- a/tests/unit/test_x11_terminal_mode.py +++ b/tests/unit/test_x11_terminal_mode.py @@ -85,6 +85,25 @@ def test_x11_key_name_mapping_for_keypad_and_capslock(): assert x11.keysym_name_to_key_name("_") == "KEY_MINUS" +@pytest.mark.unit +@pytest.mark.parametrize( + ("keysym_name", "key_name"), + [ + ("BackSpace", "KEY_BACKSPACE"), + ("Tab", "KEY_TAB"), + ("Return", "KEY_ENTER"), + ("Escape", "KEY_ESC"), + ("Delete", "KEY_DELETE"), + ], +) +def test_x11_special_keysyms_use_symbolic_names(keysym_name, key_name): + x11 = X11Driver() + resolved_name = x11.keysym_to_name(XK.string_to_keysym(keysym_name)) + + assert resolved_name == keysym_name + assert x11.keysym_name_to_key_name(resolved_name) == key_name + + @pytest.mark.unit def test_x11_keycode_mapping_prefers_keypad_aliases(): x11 = X11Driver()