tui: fix resize on wezterm (#2600)
WezTerm doesn't respond to cursor queries during a synchronized update, so resizing was broken there.
This commit is contained in:
@@ -138,6 +138,12 @@ enum ResumeAction {
|
||||
RestoreAlt = 2,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
enum PreparedResumeAction {
|
||||
RestoreAltScreen,
|
||||
RealignViewport(ratatui::layout::Rect),
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn take_resume_action(pending: &AtomicU8) -> ResumeAction {
|
||||
match pending.swap(ResumeAction::None as u8, Ordering::Relaxed) {
|
||||
@@ -333,23 +339,37 @@ impl Tui {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn apply_resume_action(&mut self, action: ResumeAction) -> Result<()> {
|
||||
fn prepare_resume_action(
|
||||
&mut self,
|
||||
action: ResumeAction,
|
||||
) -> Result<Option<PreparedResumeAction>> {
|
||||
match action {
|
||||
ResumeAction::RealignInline => {
|
||||
let cursor_pos = self.terminal.get_cursor_position()?;
|
||||
self.terminal
|
||||
.set_viewport_area(ratatui::layout::Rect::new(0, cursor_pos.y, 0, 0));
|
||||
Ok(Some(PreparedResumeAction::RealignViewport(
|
||||
ratatui::layout::Rect::new(0, cursor_pos.y, 0, 0),
|
||||
)))
|
||||
}
|
||||
ResumeAction::RestoreAlt => {
|
||||
// When we're resuming from alt screen, we need to save what the cursor position
|
||||
// _was_ when we resumed. That way, when we leave the alt screen, we can restore
|
||||
// the cursor to the new position.
|
||||
if let Ok((_x, y)) = crossterm::cursor::position()
|
||||
&& let Some(saved) = self.alt_saved_viewport.as_mut()
|
||||
{
|
||||
saved.y = y;
|
||||
}
|
||||
let _ = execute!(self.terminal.backend_mut(), EnterAlternateScreen);
|
||||
Ok(Some(PreparedResumeAction::RestoreAltScreen))
|
||||
}
|
||||
ResumeAction::None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn apply_prepared_resume_action(&mut self, prepared: PreparedResumeAction) -> Result<()> {
|
||||
match prepared {
|
||||
PreparedResumeAction::RealignViewport(area) => {
|
||||
self.terminal.set_viewport_area(area);
|
||||
}
|
||||
PreparedResumeAction::RestoreAltScreen => {
|
||||
execute!(self.terminal.backend_mut(), EnterAlternateScreen)?;
|
||||
if let Ok(size) = self.terminal.size() {
|
||||
self.terminal.set_viewport_area(ratatui::layout::Rect::new(
|
||||
0,
|
||||
@@ -360,13 +380,10 @@ impl Tui {
|
||||
self.terminal.clear()?;
|
||||
}
|
||||
}
|
||||
ResumeAction::None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Public suspend() removed; Ctrl+Z is handled internally via event_stream + draw.
|
||||
|
||||
/// Enter alternate screen and expand the viewport to full terminal size, saving the current
|
||||
/// inline viewport for restoration when leaving.
|
||||
pub fn enter_alt_screen(&mut self) -> Result<()> {
|
||||
@@ -405,12 +422,13 @@ impl Tui {
|
||||
height: u16,
|
||||
draw_fn: impl FnOnce(&mut custom_terminal::Frame),
|
||||
) -> Result<()> {
|
||||
std::io::stdout().sync_update(|_| {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// Apply any post-resume action before layout/clear/draw.
|
||||
self.apply_resume_action(take_resume_action(&self.resume_pending))?;
|
||||
}
|
||||
// Precompute any viewport updates that need a cursor-position query before entering
|
||||
// the synchronized update, to avoid racing with the event reader.
|
||||
let mut pending_viewport_area: Option<ratatui::layout::Rect> = None;
|
||||
#[cfg(unix)]
|
||||
let mut prepared_resume =
|
||||
self.prepare_resume_action(take_resume_action(&self.resume_pending))?;
|
||||
{
|
||||
let terminal = &mut self.terminal;
|
||||
let screen_size = terminal.size()?;
|
||||
let last_known_screen_size = terminal.last_known_screen_size;
|
||||
@@ -419,15 +437,27 @@ impl Tui {
|
||||
let last_known_cursor_pos = terminal.last_known_cursor_pos;
|
||||
if cursor_pos.y != last_known_cursor_pos.y {
|
||||
let cursor_delta = cursor_pos.y as i32 - last_known_cursor_pos.y as i32;
|
||||
|
||||
let new_viewport_area = terminal.viewport_area.offset(Offset {
|
||||
x: 0,
|
||||
y: cursor_delta,
|
||||
});
|
||||
terminal.set_viewport_area(new_viewport_area);
|
||||
terminal.clear()?;
|
||||
pending_viewport_area = Some(new_viewport_area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::io::stdout().sync_update(|_| {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if let Some(prepared) = prepared_resume.take() {
|
||||
self.apply_prepared_resume_action(prepared)?;
|
||||
}
|
||||
}
|
||||
let terminal = &mut self.terminal;
|
||||
if let Some(new_area) = pending_viewport_area.take() {
|
||||
terminal.set_viewport_area(new_area);
|
||||
terminal.clear()?;
|
||||
}
|
||||
|
||||
let size = terminal.size()?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user