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,
|
RestoreAlt = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
enum PreparedResumeAction {
|
||||||
|
RestoreAltScreen,
|
||||||
|
RealignViewport(ratatui::layout::Rect),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn take_resume_action(pending: &AtomicU8) -> ResumeAction {
|
fn take_resume_action(pending: &AtomicU8) -> ResumeAction {
|
||||||
match pending.swap(ResumeAction::None as u8, Ordering::Relaxed) {
|
match pending.swap(ResumeAction::None as u8, Ordering::Relaxed) {
|
||||||
@@ -333,23 +339,37 @@ impl Tui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn apply_resume_action(&mut self, action: ResumeAction) -> Result<()> {
|
fn prepare_resume_action(
|
||||||
|
&mut self,
|
||||||
|
action: ResumeAction,
|
||||||
|
) -> Result<Option<PreparedResumeAction>> {
|
||||||
match action {
|
match action {
|
||||||
ResumeAction::RealignInline => {
|
ResumeAction::RealignInline => {
|
||||||
let cursor_pos = self.terminal.get_cursor_position()?;
|
let cursor_pos = self.terminal.get_cursor_position()?;
|
||||||
self.terminal
|
Ok(Some(PreparedResumeAction::RealignViewport(
|
||||||
.set_viewport_area(ratatui::layout::Rect::new(0, cursor_pos.y, 0, 0));
|
ratatui::layout::Rect::new(0, cursor_pos.y, 0, 0),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
ResumeAction::RestoreAlt => {
|
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()
|
if let Ok((_x, y)) = crossterm::cursor::position()
|
||||||
&& let Some(saved) = self.alt_saved_viewport.as_mut()
|
&& let Some(saved) = self.alt_saved_viewport.as_mut()
|
||||||
{
|
{
|
||||||
saved.y = y;
|
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() {
|
if let Ok(size) = self.terminal.size() {
|
||||||
self.terminal.set_viewport_area(ratatui::layout::Rect::new(
|
self.terminal.set_viewport_area(ratatui::layout::Rect::new(
|
||||||
0,
|
0,
|
||||||
@@ -360,13 +380,10 @@ impl Tui {
|
|||||||
self.terminal.clear()?;
|
self.terminal.clear()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResumeAction::None => {}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
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
|
/// Enter alternate screen and expand the viewport to full terminal size, saving the current
|
||||||
/// inline viewport for restoration when leaving.
|
/// inline viewport for restoration when leaving.
|
||||||
pub fn enter_alt_screen(&mut self) -> Result<()> {
|
pub fn enter_alt_screen(&mut self) -> Result<()> {
|
||||||
@@ -405,12 +422,13 @@ impl Tui {
|
|||||||
height: u16,
|
height: u16,
|
||||||
draw_fn: impl FnOnce(&mut custom_terminal::Frame),
|
draw_fn: impl FnOnce(&mut custom_terminal::Frame),
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
std::io::stdout().sync_update(|_| {
|
// 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)]
|
#[cfg(unix)]
|
||||||
|
let mut prepared_resume =
|
||||||
|
self.prepare_resume_action(take_resume_action(&self.resume_pending))?;
|
||||||
{
|
{
|
||||||
// Apply any post-resume action before layout/clear/draw.
|
|
||||||
self.apply_resume_action(take_resume_action(&self.resume_pending))?;
|
|
||||||
}
|
|
||||||
let terminal = &mut self.terminal;
|
let terminal = &mut self.terminal;
|
||||||
let screen_size = terminal.size()?;
|
let screen_size = terminal.size()?;
|
||||||
let last_known_screen_size = terminal.last_known_screen_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;
|
let last_known_cursor_pos = terminal.last_known_cursor_pos;
|
||||||
if cursor_pos.y != last_known_cursor_pos.y {
|
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 cursor_delta = cursor_pos.y as i32 - last_known_cursor_pos.y as i32;
|
||||||
|
|
||||||
let new_viewport_area = terminal.viewport_area.offset(Offset {
|
let new_viewport_area = terminal.viewport_area.offset(Offset {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: cursor_delta,
|
y: cursor_delta,
|
||||||
});
|
});
|
||||||
terminal.set_viewport_area(new_viewport_area);
|
pending_viewport_area = Some(new_viewport_area);
|
||||||
terminal.clear()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()?;
|
let size = terminal.size()?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user