1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 13:52:55 +03:00

termwiz: windows: fixup viewport handling

Some windows APIs have inclusive dimensions and some exclusive;
we were off by one for the height of the display which led to some
weirdness with eg: `sp` and the line editor.

When it comes to scrolling: if the scroll request is for the entire
viewport then we simply adjust the viewport; this is desirable because
it allows data to scroll back into the history in the native console.
This commit is contained in:
Wez Furlong 2020-04-06 09:33:52 -07:00
parent 290bc5567e
commit 92fa32695c
2 changed files with 65 additions and 65 deletions

View File

@ -152,6 +152,21 @@ impl ScreenBuffer {
end end
} }
fn do_cursor_y_scroll<B: ConsoleOutputHandle + Write>(
&mut self,
out: &mut B,
) -> anyhow::Result<()> {
if self.cursor_y >= self.rows {
self.dirty = true;
let lines_to_scroll = self.cursor_y.saturating_sub(self.rows) + 1;
self.scroll(0, self.rows, -1 * lines_to_scroll as isize, out)?;
self.dirty = true;
self.cursor_y -= lines_to_scroll;
assert!(self.cursor_y < self.rows);
}
Ok(())
}
fn set_cursor<B: ConsoleOutputHandle + Write>( fn set_cursor<B: ConsoleOutputHandle + Write>(
&mut self, &mut self,
x: usize, x: usize,
@ -161,12 +176,7 @@ impl ScreenBuffer {
self.cursor_x = x; self.cursor_x = x;
self.cursor_y = y; self.cursor_y = y;
if self.cursor_y >= self.rows { self.do_cursor_y_scroll(out)?;
let lines_to_scroll = self.cursor_y.saturating_sub(self.rows) + 1;
self.scroll_up(0, self.rows, lines_to_scroll, out)?;
// Adjust cursor by an extra position to compensate for the scroll
self.cursor_y -= lines_to_scroll + 1;
}
// Make sure we mark dirty after we've scrolled! // Make sure we mark dirty after we've scrolled!
self.dirty = true; self.dirty = true;
@ -188,28 +198,17 @@ impl ScreenBuffer {
} }
'\n' => { '\n' => {
self.cursor_y += 1; self.cursor_y += 1;
if self.cursor_y >= self.rows { self.do_cursor_y_scroll(out)?;
self.dirty = true;
self.scroll_up(0, self.rows, 1 + self.cursor_y - self.rows, out)?;
self.dirty = true;
self.cursor_y = self.rows - 1;
assert!(self.cursor_y < self.rows);
}
} }
c => { c => {
if self.cursor_x == self.cols { if self.cursor_x == self.cols {
self.cursor_y += 1; self.cursor_y += 1;
self.cursor_x = 0; self.cursor_x = 0;
if self.cursor_y >= self.rows {
self.dirty = true;
self.scroll_up(0, self.rows, 1 + self.cursor_y - self.rows, out)?;
self.dirty = true;
self.cursor_y = self.rows - 1;
assert!(self.cursor_y < self.rows);
}
} }
self.do_cursor_y_scroll(out)?;
let idx = self.cursor_idx(); let idx = self.cursor_idx();
let mut cell = &mut self.buf[idx]; let mut cell = &mut self.buf[idx];
cell.Attributes = attr; cell.Attributes = attr;
unsafe { unsafe {
@ -251,49 +250,41 @@ impl ScreenBuffer {
Ok(()) Ok(())
} }
fn scroll_up<B: ConsoleOutputHandle + Write>( fn scroll<B: ConsoleOutputHandle + Write>(
&mut self, &mut self,
first_row: usize, first_row: usize,
region_size: usize, region_size: usize,
scroll_count: usize, scroll_count: isize,
out: &mut B, out: &mut B,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if region_size > 0 { if region_size > 0 && scroll_count != 0 {
self.flush_screen(out)?; self.flush_screen(out)?;
let info = out.get_buffer_info()?; let info = out.get_buffer_info()?;
out.scroll_region(
info.srWindow.Left,
info.srWindow.Top + first_row as i16,
info.srWindow.Right,
info.srWindow.Top + first_row as i16 + region_size as i16,
0,
-(scroll_count as i16),
self.pending_attr,
)?;
self.reread_buffer(out)?;
}
Ok(())
}
fn scroll_down<B: ConsoleOutputHandle + Write>( // Scroll the full width of the window, always.
&mut self, let left = 0;
first_row: usize, let right = info.dwSize.X - 1;
region_size: usize,
scroll_count: usize, // We're only doing vertical scrolling
out: &mut B, let dx = 0;
) -> anyhow::Result<()> { let dy = scroll_count as i16;
if region_size > 0 {
self.flush_screen(out)?; if first_row == 0 && region_size == self.rows {
let info = out.get_buffer_info()?; // We're scrolling the whole screen, so let it scroll
out.scroll_region( // up into the scrollback
info.srWindow.Left, out.set_viewport(
info.srWindow.Top + first_row as i16, info.srWindow.Left,
info.srWindow.Right, info.srWindow.Top - dy,
info.srWindow.Top + first_row as i16 + region_size as i16, info.srWindow.Right,
0, info.srWindow.Bottom - dy,
scroll_count as i16, )?;
self.pending_attr, } else {
)?; // We're just scrolling a region within the window
let top = info.srWindow.Top + first_row as i16;
let bottom = top + region_size.saturating_sub(1) as i16;
out.scroll_region(left, top, right, bottom, dx, dy, self.pending_attr)?;
}
self.reread_buffer(out)?; self.reread_buffer(out)?;
} }
Ok(()) Ok(())
@ -310,14 +301,12 @@ impl WindowsConsoleRenderer {
let info = out.get_buffer_info()?; let info = out.get_buffer_info()?;
let cols = info.dwSize.X as usize; let cols = info.dwSize.X as usize;
let rows = info.srWindow.Bottom as usize - info.srWindow.Top as usize; let rows = 1 + info.srWindow.Bottom as usize - info.srWindow.Top as usize;
let mut buffer = ScreenBuffer { let mut buffer = ScreenBuffer {
buf: out.get_buffer_contents()?, buf: out.get_buffer_contents()?,
cursor_x: info.dwCursorPosition.X as usize, cursor_x: info.dwCursorPosition.X as usize,
cursor_y: (info.dwCursorPosition.Y as usize) cursor_y: (info.dwCursorPosition.Y as usize).saturating_sub(info.srWindow.Top as usize),
.saturating_sub(info.srWindow.Top as usize)
.min(rows - 1),
dirty: false, dirty: false,
rows, rows,
cols, cols,
@ -439,14 +428,14 @@ impl WindowsConsoleRenderer {
region_size, region_size,
scroll_count, scroll_count,
} => { } => {
buffer.scroll_up(*first_row, *region_size, *scroll_count, out)?; buffer.scroll(*first_row, *region_size, -1 * *scroll_count as isize, out)?;
} }
Change::ScrollRegionDown { Change::ScrollRegionDown {
first_row, first_row,
region_size, region_size,
scroll_count, scroll_count,
} => { } => {
buffer.scroll_down(*first_row, *region_size, *scroll_count, out)?; buffer.scroll(*first_row, *region_size, *scroll_count as isize, out)?;
} }
Change::Title(_text) => { Change::Title(_text) => {
// Don't actually render this for now. // Don't actually render this for now.

View File

@ -302,7 +302,7 @@ impl ConsoleOutputHandle for OutputHandle {
let info = self.get_buffer_info()?; let info = self.get_buffer_info()?;
let cols = info.dwSize.X as usize; let cols = info.dwSize.X as usize;
let rows = info.srWindow.Bottom as usize - info.srWindow.Top as usize; let rows = 1 + info.srWindow.Bottom as usize - info.srWindow.Top as usize;
let mut res = vec![ let mut res = vec![
CHAR_INFO { CHAR_INFO {
@ -311,7 +311,12 @@ impl ConsoleOutputHandle for OutputHandle {
}; };
cols * rows cols * rows
]; ];
let mut read_region = info.srWindow.clone(); let mut read_region = SMALL_RECT {
Left: 0,
Right: info.dwSize.X - 1,
Top: info.srWindow.Top,
Bottom: info.srWindow.Bottom,
};
unsafe { unsafe {
if ReadConsoleOutputW( if ReadConsoleOutputW(
self.handle.as_raw_handle() as *mut _, self.handle.as_raw_handle() as *mut _,
@ -334,13 +339,19 @@ impl ConsoleOutputHandle for OutputHandle {
let info = self.get_buffer_info()?; let info = self.get_buffer_info()?;
let cols = info.dwSize.X as usize; let cols = info.dwSize.X as usize;
let rows = info.srWindow.Bottom as usize - info.srWindow.Top as usize; let rows = 1 + info.srWindow.Bottom as usize - info.srWindow.Top as usize;
anyhow::ensure!( anyhow::ensure!(
rows * cols == buffer.len(), rows * cols == buffer.len(),
"buffer size doesn't match screen size" "buffer size doesn't match screen size"
); );
let mut write_region = info.srWindow.clone(); let mut write_region = SMALL_RECT {
Left: 0,
Right: info.dwSize.X - 1,
Top: info.srWindow.Top,
Bottom: info.srWindow.Bottom,
};
unsafe { unsafe {
if WriteConsoleOutputW( if WriteConsoleOutputW(
self.handle.as_raw_handle() as *mut _, self.handle.as_raw_handle() as *mut _,