Handle alternate screen modes
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user