textual icon indicating copy to clipboard operation
textual copied to clipboard

On Windows 11, pressing the 'escape' key in browser via textual-serve doesn't register until the mouse is moved or the 'escape' key is pressed again

Open Quantumatum opened this issue 7 months ago • 2 comments

On Windows 11 (and possibly other versions too), pressing the 'escape' key in browser via textual-serve doesn't register until the mouse is moved or until the 'escape' key is pressed again. Also, after pressing escape once, all further key strokes are "ignored" until the mouse is moved or the 'escape' key is pressed again, only then do all the keystrokes register. I'm not sure what is ultimately responsible for this: textual, the browser, or textual-serve.

Here's a tiny MRE to demonstrate this bug:

from textual.app import App
from textual import events

class TestApp(App):
    def on_key(self, event: events.Key) -> None:
        self.notify(str(event.key))

if __name__ == "__main__":
    TestApp().run()

textual serve mre.py

The following sequence can be followed: Esc, a, b, c, <mouse move> OR Esc. The a, b, c notifications will not show till the end of the sequence. Note, pressing Esc a second time causes further weirdness that moving the mouse doesn't.

At the time of this report, I'm running: textual-dev 1.7.0, textual-serve 1.1.2 and textual 4.0.0

textual diagnose:

Versions

Name Value
Textual 4.0.0
Rich 14.0.0

Python

Name Value
Version 3.11.0
Implementation CPython
Compiler MSC v.1933 64 bit (AMD64)
Executable C:\Users\quantum\PycharmProjects\textual_mre\venv\Scripts\python.exe

Operating System

Name Value
System Windows
Release 10
Version 10.0.22631

Terminal

Name Value
Terminal Application Unknown
TERM Not set
COLORTERM Not set
FORCE_COLOR Not set
NO_COLOR Not set

Rich Console options

Name Value
size width=135, height=32
legacy_windows False
min_width 1
max_width 135
is_terminal True
encoding utf-8
max_height 32
justify None
overflow None
no_wrap False
highlight None
markup None
height None

Quantumatum avatar Jul 17 '25 17:07 Quantumatum

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This project is developed and maintained by Will McGugan. Consider sponsoring Will's work on this project (and others).

This is an automated reply, generated by FAQtory

github-actions[bot] avatar Jul 17 '25 17:07 github-actions[bot]

I'm able to reproduce this on Windows 11.

It appears to be an issue with ESC handling under Windows. The front-end (JavaScript) sends a data package with payload "\x1b" to textual_serve; textual_serve then sends the same payload to textual app. However, the latter would treat the ESC as the leading byte of a sequence, and wait until the full sequence is received. This is handled here.

Normally, the VT sequence parser contains logic to wait for no more than 100 milliseconds for the sequence after ESC to arrive before treating the ESC as an ESC. This mechanism appears to not work under Windows. It might be due to the InputReader implementation under Windows having no timeout support; the Linux counterpart has.

A possible workaround could be to have the front-end send the ESC as a meta packet (like focus, resize) instead of a data packet. Another possibility is to rely on the behavior that an escape sequence never crosses websocket packet boundary, and to treat a standalone ESC as a key press by itself without waiting for the subsequent characters. Yet another possibility is to fix the blocking read under Windows; this is done by the linked PR #6031.

fancidev avatar Aug 07 '25 09:08 fancidev