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:
parent
290bc5567e
commit
92fa32695c
@ -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.
|
||||||
|
@ -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 _,
|
||||||
|
Loading…
Reference in New Issue
Block a user