fix: proper terminal buffer save/restore with full pyte screen state
Save complete pyte screen state (characters, colors, attributes, cursor position, modes) via pickle serialization instead of just plain text. Restore via direct buffer manipulation + full redraw. Fixes broken/garbled layout when switching between servers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -362,21 +362,81 @@ class TerminalWidget(tk.Frame):
|
||||
return self._cols, self._rows
|
||||
|
||||
def get_current_buffer(self) -> bytes:
|
||||
"""Export current pyte screen content as ANSI bytes for session pool preservation."""
|
||||
screen = self._screen
|
||||
lines = []
|
||||
for row in range(screen.lines):
|
||||
line = screen.buffer[row]
|
||||
chars = []
|
||||
for col in range(screen.columns):
|
||||
chars.append(line[col].data)
|
||||
# Strip trailing spaces
|
||||
text = "".join(chars).rstrip()
|
||||
lines.append(text)
|
||||
# Remove trailing empty lines
|
||||
while lines and not lines[-1]:
|
||||
lines.pop()
|
||||
return "\n".join(lines).encode("utf-8")
|
||||
"""Export full pyte screen state as serialized data for session pool."""
|
||||
import pickle
|
||||
|
||||
screen_state = {
|
||||
'buffer': {},
|
||||
'cursor_x': self._screen.cursor.x,
|
||||
'cursor_y': self._screen.cursor.y,
|
||||
'cursor_attrs': self._screen.cursor.attrs,
|
||||
'cursor_hidden': self._screen.cursor.hidden,
|
||||
'mode': self._screen.mode.copy(),
|
||||
'columns': self._screen.columns,
|
||||
'lines': self._screen.lines,
|
||||
}
|
||||
|
||||
for y in range(self._screen.lines):
|
||||
line_dict = {}
|
||||
for x in range(self._screen.columns):
|
||||
c = self._screen.buffer[y][x]
|
||||
line_dict[x] = {
|
||||
'data': c.data, 'fg': c.fg, 'bg': c.bg,
|
||||
'bold': c.bold, 'italics': c.italics,
|
||||
'underscore': c.underscore, 'reverse': c.reverse,
|
||||
'strikethrough': c.strikethrough, 'width': c.width,
|
||||
}
|
||||
screen_state['buffer'][y] = line_dict
|
||||
|
||||
return pickle.dumps(screen_state)
|
||||
|
||||
def restore_buffer(self, buffer_data: bytes):
|
||||
"""Restore full pyte screen state from serialized data."""
|
||||
import pickle
|
||||
|
||||
try:
|
||||
state = pickle.loads(buffer_data)
|
||||
if not isinstance(state, dict) or 'buffer' not in state:
|
||||
return
|
||||
|
||||
cols, rows = self._screen.columns, self._screen.lines
|
||||
self._screen.reset()
|
||||
self._screen.resize(rows, cols)
|
||||
|
||||
for y, line_dict in state['buffer'].items():
|
||||
if y >= self._screen.lines:
|
||||
continue
|
||||
for x, cd in line_dict.items():
|
||||
if x >= self._screen.columns:
|
||||
continue
|
||||
ch = self._screen.buffer[y][x]
|
||||
ch.data = cd['data']
|
||||
ch.fg = cd['fg']
|
||||
ch.bg = cd['bg']
|
||||
ch.bold = cd['bold']
|
||||
ch.italics = cd['italics']
|
||||
ch.underscore = cd['underscore']
|
||||
ch.reverse = cd['reverse']
|
||||
ch.strikethrough = cd['strikethrough']
|
||||
ch.width = cd['width']
|
||||
|
||||
self._screen.cursor.x = state.get('cursor_x', 0)
|
||||
self._screen.cursor.y = state.get('cursor_y', 0)
|
||||
if 'cursor_attrs' in state:
|
||||
self._screen.cursor.attrs = state['cursor_attrs']
|
||||
if 'cursor_hidden' in state:
|
||||
self._screen.cursor.hidden = state['cursor_hidden']
|
||||
if 'mode' in state:
|
||||
self._screen.mode.clear()
|
||||
self._screen.mode.update(state['mode'])
|
||||
|
||||
# Force full redraw
|
||||
self._screen.dirty.update(range(self._screen.lines))
|
||||
self._prev_buffer.clear()
|
||||
self._render()
|
||||
|
||||
except Exception:
|
||||
self.reset()
|
||||
|
||||
# ── Rendering ──────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user