Handle alternate screen modes

This commit is contained in:
GitHub Copilot
2026-01-31 16:49:54 +00:00
parent 6d613f008d
commit f12c9901d7
2 changed files with 45 additions and 6 deletions
+17 -5
View File
@@ -19,8 +19,10 @@ import pyte
if TYPE_CHECKING: if TYPE_CHECKING:
from pyte.screens import Char 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 = 1049 << 5
DECALTBUF_1047 = 1047 << 5
DECALTBUF_1048 = 1048 << 5
class AltScreen(pyte.Screen): class AltScreen(pyte.Screen):
@@ -74,10 +76,20 @@ class AltScreen(pyte.Screen):
# Mark all lines as dirty for re-render # Mark all lines as dirty for re-render
self.dirty.update(range(self.lines)) 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: def set_mode(self, *modes: int, **kwargs: Any) -> None:
"""Set (enable) modes, with special handling for alternate screen buffer.""" """Set (enable) modes, with special handling for alternate screen buffer."""
# Check if we're entering alternate screen mode (private mode 1049) # Check if we're entering alternate screen mode (private mode 1047/1048/1049)
if kwargs.get("private") and 1049 in modes and DECALTBUF not in self.mode: if kwargs.get("private") and self._is_alt_buffer_mode(modes) and not self._has_alt_buffer_enabled():
# Save main screen before switching # Save main screen before switching
self._save_main_screen() self._save_main_screen()
# Clear screen for alternate buffer # Clear screen for alternate buffer
@@ -89,8 +101,8 @@ class AltScreen(pyte.Screen):
def reset_mode(self, *modes: int, **kwargs: Any) -> None: def reset_mode(self, *modes: int, **kwargs: Any) -> None:
"""Reset (disable) modes, with special handling for alternate screen buffer.""" """Reset (disable) modes, with special handling for alternate screen buffer."""
# Check if we're leaving alternate screen mode (private mode 1049) # Check if we're leaving alternate screen mode (private mode 1047/1048/1049)
if kwargs.get("private") and 1049 in modes and DECALTBUF in self.mode: 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 # Will be removed by parent, restore main screen after
super().reset_mode(*modes, **kwargs) super().reset_mode(*modes, **kwargs)
self._restore_main_screen() self._restore_main_screen()
+28 -1
View File
@@ -1,8 +1,9 @@
"""Tests for the AltScreen class with alternate screen buffer support.""" """Tests for the AltScreen class with alternate screen buffer support."""
import pyte import pyte
import pytest
from webterm.alt_screen import DECALTBUF, AltScreen from webterm.alt_screen import DECALTBUF, DECALTBUF_1047, DECALTBUF_1048, AltScreen
class TestAltScreen: class TestAltScreen:
@@ -59,6 +60,32 @@ class TestAltScreen:
stream.feed("\x1b[?1049l") stream.feed("\x1b[?1049l")
assert DECALTBUF not in screen.mode 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): def test_multiple_alt_screen_switches(self):
"""Test multiple switches between main and alternate screen.""" """Test multiple switches between main and alternate screen."""
screen = AltScreen(40, 10) screen = AltScreen(40, 10)