Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backend): add window size api #276

Merged
merged 1 commit into from Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 20 additions & 5 deletions src/backend/crossterm.rs
Expand Up @@ -18,9 +18,10 @@
};

use crate::{
backend::{Backend, ClearType},
backend::{Backend, ClearType, WindowSize},
buffer::Cell,
layout::Rect,
layout::Size,
prelude::Rect,
style::{Color, Modifier},
};

Expand Down Expand Up @@ -169,12 +170,26 @@
}

fn size(&self) -> io::Result<Rect> {
let (width, height) =
terminal::size().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

let (width, height) = terminal::size()?;

Check warning on line 173 in src/backend/crossterm.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/crossterm.rs#L173

Added line #L173 was not covered by tests
Ok(Rect::new(0, 0, width, height))
}

fn window_size(&mut self) -> Result<WindowSize, io::Error> {

Check warning on line 177 in src/backend/crossterm.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/crossterm.rs#L177

Added line #L177 was not covered by tests
let crossterm::terminal::WindowSize {
columns,
rows,
width,
height,
} = terminal::window_size()?;
Ok(WindowSize {
columns_rows: Size {
width: columns,
height: rows,
},
pixels: Size { width, height },
})
}

Check warning on line 191 in src/backend/crossterm.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/crossterm.rs#L179-L191

Added lines #L179 - L191 were not covered by tests

fn flush(&mut self) -> io::Result<()> {
self.buffer.flush()
}
Expand Down
23 changes: 21 additions & 2 deletions src/backend/mod.rs
Expand Up @@ -29,7 +29,7 @@ use std::io;

use strum::{Display, EnumString};

use crate::{buffer::Cell, layout::Rect};
use crate::{buffer::Cell, layout::Size, prelude::Rect};

#[cfg(feature = "termion")]
mod termion;
Expand Down Expand Up @@ -60,6 +60,18 @@ pub enum ClearType {
UntilNewLine,
}

/// The window sizes in columns,rows and optionally pixel width,height.
pub struct WindowSize {
/// Size in character/cell columents,rows.
pub columns_rows: Size,
/// Size in pixel width,height.
///
/// The `pixels` fields may not be implemented by all terminals and return `0,0`.
/// See https://man7.org/linux/man-pages/man4/tty_ioctl.4.html under section
/// "Get and set window size" / TIOCGWINSZ where the fields are commented as "unused".
pub pixels: Size,
}

/// The `Backend` trait provides an abstraction over different terminal libraries.
/// It defines the methods required to draw content, manipulate the cursor, and
/// clear the terminal screen.
Expand Down Expand Up @@ -111,9 +123,16 @@ pub trait Backend {
}
}

/// Get the size of the terminal screen as a [`Rect`].
/// Get the size of the terminal screen in columns/rows as a [`Rect`].
fn size(&self) -> Result<Rect, io::Error>;

/// Get the size of the terminal screen in columns/rows and pixels as [`WindowSize`].
///
/// The reason for this not returning only the pixel size, given the redundancy with the
/// `size()` method, is that the underlying backends most likely get both values with one
/// syscall, and the user is also most likely to need columns,rows together with pixel size.
fn window_size(&mut self) -> Result<WindowSize, io::Error>;

/// Flush any buffered content to the terminal screen.
fn flush(&mut self) -> Result<(), io::Error>;
}
Expand Down
11 changes: 9 additions & 2 deletions src/backend/termion.rs
Expand Up @@ -10,9 +10,9 @@
};

use crate::{
backend::{Backend, ClearType},
backend::{Backend, ClearType, WindowSize},
buffer::Cell,
layout::Rect,
prelude::Rect,
style::{Color, Modifier},
};

Expand Down Expand Up @@ -160,6 +160,13 @@
Ok(Rect::new(0, 0, terminal.0, terminal.1))
}

fn window_size(&mut self) -> Result<WindowSize, io::Error> {
Ok(WindowSize {
columns_rows: termion::terminal_size()?.into(),
pixels: termion::terminal_size_pixels()?.into(),

Check warning on line 166 in src/backend/termion.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termion.rs#L163-L166

Added lines #L163 - L166 were not covered by tests
})
}

Check warning on line 168 in src/backend/termion.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termion.rs#L168

Added line #L168 was not covered by tests

fn flush(&mut self) -> io::Result<()> {
self.stdout.flush()
}
Expand Down
49 changes: 32 additions & 17 deletions src/backend/termwiz.rs
Expand Up @@ -11,13 +11,14 @@
cell::{AttributeChange, Blink, Intensity, Underline},
color::{AnsiColor, ColorAttribute, SrgbaTuple},
surface::{Change, CursorVisibility, Position},
terminal::{buffered::BufferedTerminal, SystemTerminal, Terminal},
terminal::{buffered::BufferedTerminal, ScreenSize, SystemTerminal, Terminal},
};

use crate::{
backend::Backend,
backend::{Backend, WindowSize},
buffer::Cell,
layout::Rect,
layout::Size,
prelude::Rect,
style::{Color, Modifier},
};

Expand Down Expand Up @@ -169,22 +170,31 @@
}

fn size(&self) -> Result<Rect, io::Error> {
let (term_width, term_height) = self.buffered_terminal.dimensions();
let max = u16::max_value();
Ok(Rect::new(
0,
0,
if term_width > usize::from(max) {
max
} else {
term_width as u16
let (cols, rows) = self.buffered_terminal.dimensions();
Ok(Rect::new(0, 0, u16_max(cols), u16_max(rows)))
}

Check warning on line 175 in src/backend/termwiz.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termwiz.rs#L173-L175

Added lines #L173 - L175 were not covered by tests

fn window_size(&mut self) -> Result<WindowSize, io::Error> {

Check warning on line 177 in src/backend/termwiz.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termwiz.rs#L177

Added line #L177 was not covered by tests
let ScreenSize {
cols,
rows,
xpixel,
ypixel,
} = self
.buffered_terminal
.terminal()
.get_screen_size()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(WindowSize {
columns_rows: Size {
width: u16_max(cols),
height: u16_max(rows),

Check warning on line 191 in src/backend/termwiz.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termwiz.rs#L179-L191

Added lines #L179 - L191 were not covered by tests
},
if term_height > usize::from(max) {
max
} else {
term_height as u16
pixels: Size {
width: u16_max(xpixel),
height: u16_max(ypixel),

Check warning on line 195 in src/backend/termwiz.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termwiz.rs#L193-L195

Added lines #L193 - L195 were not covered by tests
},
))
})

Check warning on line 197 in src/backend/termwiz.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termwiz.rs#L197

Added line #L197 was not covered by tests
}

fn flush(&mut self) -> Result<(), io::Error> {
Expand Down Expand Up @@ -221,3 +231,8 @@
}
}
}

#[inline]
fn u16_max(i: usize) -> u16 {
u16::try_from(i).unwrap_or(u16::MAX)
}

Check warning on line 238 in src/backend/termwiz.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/termwiz.rs#L236-L238

Added lines #L236 - L238 were not covered by tests
16 changes: 14 additions & 2 deletions src/backend/test.rs
Expand Up @@ -9,9 +9,9 @@
use unicode_width::UnicodeWidthStr;

use crate::{
backend::Backend,
backend::{Backend, WindowSize},
buffer::{Buffer, Cell},
layout::Rect,
layout::{Rect, Size},
};

/// A backend used for the integration tests.
Expand Down Expand Up @@ -179,6 +179,18 @@
Ok(Rect::new(0, 0, self.width, self.height))
}

fn window_size(&mut self) -> Result<WindowSize, io::Error> {
// Some arbitrary window pixel size, probably doesn't need much testing.
static WINDOW_PIXEL_SIZE: Size = Size {
width: 640,
height: 480,
joshka marked this conversation as resolved.
Show resolved Hide resolved
};
Ok(WindowSize {
columns_rows: (self.width, self.height).into(),
pixels: WINDOW_PIXEL_SIZE,
})
}

Check warning on line 192 in src/backend/test.rs

View check run for this annotation

Codecov / codecov/patch

src/backend/test.rs#L182-L192

Added lines #L182 - L192 were not covered by tests

fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
Expand Down
13 changes: 13 additions & 0 deletions src/layout.rs
Expand Up @@ -647,6 +647,19 @@
Ok(results)
}

/// A simple size struct
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]

Check warning on line 651 in src/layout.rs

View check run for this annotation

Codecov / codecov/patch

src/layout.rs#L651

Added line #L651 was not covered by tests
pub struct Size {
pub width: u16,
pub height: u16,
}

impl From<(u16, u16)> for Size {
fn from((width, height): (u16, u16)) -> Self {
Size { width, height }
}

Check warning on line 660 in src/layout.rs

View check run for this annotation

Codecov / codecov/patch

src/layout.rs#L658-L660

Added lines #L658 - L660 were not covered by tests
}

#[cfg(test)]
mod tests {
use strum::ParseError;
Expand Down