Open Emasoft opened 1 month ago
I really beg you to fix this issue with Textual. 🙏😩🧎🏻➡️ Textual worked in the past, so it should be possible to fix this, right? Please, this is making using a-Shell a much worse experience.
I ran a few tests, after upgrading textual and the examples directory.
calculator.py
still works, but the other commands still don't launch. A quick analysis shows that the difference is that calculator.py
ends with CalculatorApp.run(inline=True)
, while the others end with app.run()
.
If I change the app.run()
into app.run(inline=True)
, the other commands are working again. I assume Textual is trying to start a new terminal or a new window, which doesn't work here.
Hmmm. Came here to report a similar thing, but never figured out the inline part - will try!
@holzschu No, unfortunately the 'inline' mode is NOT equivalent to the normal mode.
The app looks the same, but all the mouse/touch interactions are disabled or maimed. In normal mode Terminal uses the terminal full screen mode (ANSI 'alt buffer screen'), a standard VT100 mode that is necessary to go FULL SCREEN and to use most of the interactive features, like mouse/touch drag scroll to read a document, drag a slider, drag&drop, resize a frame, pull down a drop-down menu, double-click, etc.
In inline mode those features are not working because most of the mouse/touch events are captured by the terminal screen instead, like: drag-scroll the terminal buffer, select the text, etc. So in inline mode none of those mouse/touch events arrives at the textual app. You cannot even drag a scrollbar to read a text. This is why the inline mode is almost used only for Textual script for prompt autocompletion, quick menu choices or to render a progress bar, but never in real Textual apps. In fact almost all Textual app I installed for my work use the full screen mode, not the inline mode! And even if I hack the sources and try to force them in inline mode, they are unusable!!
Textual has indeed two separate 'drivers' for that:
Full Screen Driver (default): https://github.com/Textualize/textual/blob/main/src/textual/drivers/linux_driver.py
Inline Driver: https://github.com/Textualize/textual/blob/main/src/textual/drivers/linux_inline_driver.py
You can see from the diff the different ANSI commands sent to the terminal:
https://editor.mergely.com/vPcAE2fm
It is very important to note that:
Some other iOS apps currently WORKS FINE with Textual full screen mode. For example check the iOS app WebSSH (that I use to connet remotely to ssh terminals) uses a terminal library that displays the Textual apps in full screen perfectly fine.
Please fix this issue, it is very important. I use many Textual scripts that work only in full screen mode, and now I cannot use them anymore!😭
Thanks!
I've looked into the problem. It's not going to be easy to fix: "inline" apps are not sending anything to the standard output. They're not sending the character codes to switch to alt-screen mode. They stop, for some reason, before they do anything.
@holzschu But that is exactly the intended behavior. Inline apps are using the normal screen mode, not the alternate screen. It is the alt-screen mode that makes a-Shell hang forever when launching a Textual app without the inline=True
setting. Just look at the python code inside the driver classes I posted above. ANSI commands are sent, but they are different between the two modes.
Textual setting | VT-100 mode | Full Screen | Interaction | Works in a Shell? |
---|---|---|---|---|
Textual "inline=True" | alt-screen buffer OFF (default) | NO | Mouse drag events captured by terminal. Good for small apps like autocomplete, progress bars, etc. | YES |
Textual "inline=False" (default) | alt-screen buffer ON | YES | Mouse drag events captured by Textual UI. Good for real apps like text editors, etc. | NO (but it worked before!) |
I reiterate what I just said: Textual apps running in non-inline mode are not sending anything. I have access to the debugger, I set a breakpoint in all the functions that send any kind of output to anywhere. They are never called. It's not an issue with how a-Shell deals with control characters to switch to the alt-screen, it's an issue with Textual apps not arriving at the point where they send this control character. That makes it considerably harder to fix. That was just for your information: there is no easy fix in sight.
Textual apps running in non-inline mode are not sending anything.
Are you saying that this function is never called?
def start_application_mode(self):
"""Start application mode."""
def _stop_again(*_) -> None:
"""Signal handler that will put the application back to sleep."""
os.kill(os.getpid(), signal.SIGSTOP)
# If we're working with an actual tty...
# https://github.com/Textualize/textual/issues/4104
if os.isatty(self.fileno):
# Set up handlers to ensure that, if there's a SIGTTOU or a SIGTTIN,
# we go back to sleep.
signal.signal(signal.SIGTTOU, _stop_again)
signal.signal(signal.SIGTTIN, _stop_again)
try:
# Here we perform a NOP tcsetattr. The reason for this is
# that, if we're suspended and the user has performed a `bg`
# in the shell, we'll SIGCONT *but* we won't be allowed to
# do terminal output; so rather than get into the business
# of spinning up application mode again and then finding
# out, we perform a no-consequence change and detect the
# problem right away.
termios.tcsetattr(
self.fileno, termios.TCSANOW, termios.tcgetattr(self.fileno)
)
except termios.error:
# There was an error doing the tcsetattr; there is no sense
# in carrying on because we'll be doing a SIGSTOP (see
# above).
return
finally:
# We don't need to be hooking SIGTTOU or SIGTTIN any more.
signal.signal(signal.SIGTTOU, signal.SIG_DFL)
signal.signal(signal.SIGTTIN, signal.SIG_DFL)
loop = asyncio.get_running_loop()
def send_size_event() -> None:
terminal_size = self._get_terminal_size()
width, height = terminal_size
textual_size = Size(width, height)
event = events.Resize(textual_size, textual_size)
asyncio.run_coroutine_threadsafe(
self._app._post_message(event),
loop=loop,
)
self._writer_thread = WriterThread(self._file)
self._writer_thread.start()
def on_terminal_resize(signum, stack) -> None:
send_size_event()
signal.signal(signal.SIGWINCH, on_terminal_resize)
self.write("\x1b[?1049h") # Alt screen
self._enable_mouse_support()
try:
self.attrs_before = termios.tcgetattr(self.fileno)
except termios.error:
# Ignore attribute errors.
self.attrs_before = None
try:
newattr = termios.tcgetattr(self.fileno)
except termios.error:
pass
else:
newattr[tty.LFLAG] = self._patch_lflag(newattr[tty.LFLAG])
newattr[tty.IFLAG] = self._patch_iflag(newattr[tty.IFLAG])
# VMIN defines the number of characters read at a time in
# non-canonical mode. It seems to default to 1 on Linux, but on
# Solaris and derived operating systems it defaults to 4. (This is
# because the VMIN slot is the same as the VEOF slot, which
# defaults to ASCII EOT = Ctrl-D = 4.)
newattr[tty.CC][termios.VMIN] = 1
try:
termios.tcsetattr(self.fileno, termios.TCSANOW, newattr)
except termios.error:
pass
self.write("\x1b[?25l") # Hide cursor
self.write("\x1b[?1004h") # Enable FocusIn/FocusOut.
self.write("\x1b[>1u") # https://sw.kovidgoyal.net/kitty/keyboard-protocol/
# Disambiguate escape codes https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
self.write("\x1b[=1;u")
self.flush()
self._key_thread = Thread(target=self._run_input_thread)
send_size_event()
self._key_thread.start()
self._request_terminal_sync_mode_support()
self._enable_bracketed_paste()
# Appears to fix an issue enabling mouse support in iTerm 3.5.0
self._enable_mouse_support()
# If we need to ask the app to signal that we've come back from a
# SIGTSTP...
if self._must_signal_resume:
self._must_signal_resume = False
asyncio.run_coroutine_threadsafe(
self._app._post_message(self.SignalResume()),
loop=loop,
)
If so, then this is a Textual bug! You should report to them the issue, opening a bug report: https://github.com/Textualize/textual/issues
Indeed, it either stops just before calling this function, or very soon after entering.
I don't agree with you that this is a Textual bug: a-Shell does a lot of things to emulate Unix functions that do not exist on iOS. One of these might have an uninteded side effects with what Textual does. That's why I need to investigate.
@holzschu I think it would be in the best interest of the Textual devs to support a-Shell as a platform. Even if it is not a bug by their part, they should collaborate with you in finding the cause. After all a-Shell is practically their dream target platform. a-Shell runs on touch devices with no keyboard, where the CLI would not be as useful and practical as an UI, but because there is no GUI option for programs in a-ShelI, a TUI like Textual is the only choice. By the way, I tested Textual on two ssh terminal iOS apps: WebSSH and ShellFish. Both works fine with Textual in non-inline mode. Looks like the issue must be caused by something unique of a-Shell.
I have not used textual apps in a-Shell since last year, but they always worked fine. But now (a-Shell v. 1.15) those apps do not even start. What happened? Just install Textual ( https://textual.textualize.io/getting_started/ ) and you'll see.
You can test any example from the textual repo:
https://github.com/Textualize/textual/tree/main/examples
They used to work fine, but now they do not work anymore.
You can also use this mini example to reproduce the issue:
Please fix this! 🙏