Handle alternate screen modes
This commit is contained in:
@@ -19,8 +19,10 @@ import pyte
|
||||
if TYPE_CHECKING:
|
||||
from pyte.screens import Char
|
||||
|
||||
# Private mode 1049 (alternate screen buffer) - shifted by 5 as per pyte's convention
|
||||
# Private mode alternate screen buffers (1047/1048/1049) - shifted by 5 per pyte's convention
|
||||
DECALTBUF = 1049 << 5
|
||||
DECALTBUF_1047 = 1047 << 5
|
||||
DECALTBUF_1048 = 1048 << 5
|
||||
|
||||
|
||||
class AltScreen(pyte.Screen):
|
||||
@@ -74,10 +76,20 @@ class AltScreen(pyte.Screen):
|
||||
# Mark all lines as dirty for re-render
|
||||
self.dirty.update(range(self.lines))
|
||||
|
||||
def _is_alt_buffer_mode(self, modes: tuple[int, ...]) -> bool:
|
||||
return 1047 in modes or 1048 in modes or 1049 in modes
|
||||
|
||||
def _has_alt_buffer_enabled(self) -> bool:
|
||||
return (
|
||||
DECALTBUF in self.mode
|
||||
or DECALTBUF_1047 in self.mode
|
||||
or DECALTBUF_1048 in self.mode
|
||||
)
|
||||
|
||||
def set_mode(self, *modes: int, **kwargs: Any) -> None:
|
||||
"""Set (enable) modes, with special handling for alternate screen buffer."""
|
||||
# Check if we're entering alternate screen mode (private mode 1049)
|
||||
if kwargs.get("private") and 1049 in modes and DECALTBUF not in self.mode:
|
||||
# Check if we're entering alternate screen mode (private mode 1047/1048/1049)
|
||||
if kwargs.get("private") and self._is_alt_buffer_mode(modes) and not self._has_alt_buffer_enabled():
|
||||
# Save main screen before switching
|
||||
self._save_main_screen()
|
||||
# Clear screen for alternate buffer
|
||||
@@ -89,8 +101,8 @@ class AltScreen(pyte.Screen):
|
||||
|
||||
def reset_mode(self, *modes: int, **kwargs: Any) -> None:
|
||||
"""Reset (disable) modes, with special handling for alternate screen buffer."""
|
||||
# Check if we're leaving alternate screen mode (private mode 1049)
|
||||
if kwargs.get("private") and 1049 in modes and DECALTBUF in self.mode:
|
||||
# Check if we're leaving alternate screen mode (private mode 1047/1048/1049)
|
||||
if kwargs.get("private") and self._is_alt_buffer_mode(modes) and self._has_alt_buffer_enabled():
|
||||
# Will be removed by parent, restore main screen after
|
||||
super().reset_mode(*modes, **kwargs)
|
||||
self._restore_main_screen()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Tests for the AltScreen class with alternate screen buffer support."""
|
||||
|
||||
import pyte
|
||||
import pytest
|
||||
|
||||
from webterm.alt_screen import DECALTBUF, AltScreen
|
||||
from webterm.alt_screen import DECALTBUF, DECALTBUF_1047, DECALTBUF_1048, AltScreen
|
||||
|
||||
|
||||
class TestAltScreen:
|
||||
@@ -59,6 +60,32 @@ class TestAltScreen:
|
||||
stream.feed("\x1b[?1049l")
|
||||
assert DECALTBUF not in screen.mode
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("enter_seq", "exit_seq", "mode_flag"),
|
||||
[
|
||||
("\x1b[?1047h", "\x1b[?1047l", DECALTBUF_1047),
|
||||
("\x1b[?1048h", "\x1b[?1048l", DECALTBUF_1048),
|
||||
],
|
||||
)
|
||||
def test_alternate_screen_mode_variants(self, enter_seq, exit_seq, mode_flag):
|
||||
"""Test that DECSET/DECRST 1047/1048 trigger alternate buffer handling."""
|
||||
screen = AltScreen(40, 10)
|
||||
stream = pyte.Stream(screen)
|
||||
|
||||
stream.feed("MAIN SCREEN\r\n")
|
||||
assert "MAIN SCREEN" in screen.display[0]
|
||||
|
||||
stream.feed(enter_seq)
|
||||
assert mode_flag in screen.mode
|
||||
assert screen.display[0].strip() == ""
|
||||
|
||||
stream.feed("ALT BUFFER\r\n")
|
||||
assert "ALT BUFFER" in screen.display[0]
|
||||
|
||||
stream.feed(exit_seq)
|
||||
assert mode_flag not in screen.mode
|
||||
assert "MAIN SCREEN" in screen.display[0]
|
||||
|
||||
def test_multiple_alt_screen_switches(self):
|
||||
"""Test multiple switches between main and alternate screen."""
|
||||
screen = AltScreen(40, 10)
|
||||
|
||||
Reference in New Issue
Block a user