shadow_terminal/output/
foreign.rs

1//! Define the structure of output for FFI and STDOUT users.
2
3/// The parent type for all output
4#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
5#[non_exhaustive]
6pub struct Output {
7    /// The shadow terminal's width.
8    pub width: usize,
9    /// The shadow terminal's height.
10    pub height: usize,
11    /// A 1D array of all the cells in the shadow terminal's screen.
12    pub cells: Vec<Cell>,
13    /// The shadow terminal's cursor state.
14    pub cursor: Cursor,
15    /// The title of the terminal.
16    pub title: String,
17    /// Whether the terminal is in the primary (scrolling) mode or the alternate mode.
18    pub mode: super::native::ScreenMode,
19}
20
21/// An individual cell in the shadow terminal's screen.
22#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
23#[non_exhaustive]
24pub struct Cell {
25    /// The text contents of the cell.
26    pub text: String,
27    /// The foreground colour of the cell.
28    pub foreground: Color,
29    /// The background colour of the cell.
30    pub background: Color,
31}
32
33/// The colour of a cell's foreground or background.
34#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
35#[non_exhaustive]
36pub enum Color {
37    /// The colour is the default foreground or background colour.
38    Default,
39    /// The colour is from the terminal's palette.
40    PaletteIndex(u8),
41    /// A true RGB colour.
42    TrueColor((f32, f32, f32)),
43}
44
45/// An individual cell in the shadow terminal's screen.
46#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema, Debug, PartialEq, Eq)]
47#[non_exhaustive]
48pub struct Cursor {
49    /// Position of the cursor. 0-indexed. `0,0` is in the top-left.
50    pub position: (usize, usize),
51    /// The shape of the cursor, and if it's blinking or not.
52    pub shape: Option<termwiz::surface::CursorShape>,
53    /// Whether the cursor is visible or hidden.
54    pub visibility: termwiz::surface::CursorVisibility,
55}
56
57impl From<termwiz::color::ColorAttribute> for Color {
58    #[inline]
59    fn from(attribute: termwiz::color::ColorAttribute) -> Self {
60        match attribute {
61            termwiz::color::ColorAttribute::TrueColorWithPaletteFallback(srgba_tuple, _)
62            | termwiz::color::ColorAttribute::TrueColorWithDefaultFallback(srgba_tuple) => {
63                Self::TrueColor((srgba_tuple.0, srgba_tuple.1, srgba_tuple.2))
64            }
65            termwiz::color::ColorAttribute::PaletteIndex(index) => Self::PaletteIndex(index),
66            termwiz::color::ColorAttribute::Default => Self::Default,
67        }
68    }
69}
70
71impl Output {
72    /// Convert native output to a structure more suitable for non-Rust users.
73    ///
74    /// # Errors
75    /// If trying to convert the scrollback.
76    #[inline]
77    pub fn convert_to_foreign(
78        native: super::native::CompleteSurface,
79    ) -> Result<Self, crate::errors::ShadowTerminalError> {
80        let super::native::CompleteSurface::Screen(mut screen) = native else {
81            snafu::whatever!("Converting the scrollback hasn't been implemented yet");
82        };
83
84        let mut cells = Vec::<Cell>::new();
85        for line in screen.surface.screen_cells() {
86            for cell in line {
87                cells.push(Cell {
88                    text: cell.str().to_owned(),
89                    background: cell.attrs().foreground().into(),
90                    foreground: cell.attrs().background().into(),
91                });
92            }
93        }
94
95        screen.surface.title();
96
97        Ok(Self {
98            width: screen.surface.dimensions().0,
99            height: screen.surface.dimensions().1,
100            cells,
101            cursor: Cursor {
102                shape: screen.surface.cursor_shape(),
103                visibility: screen.surface.cursor_visibility(),
104                position: screen.surface.cursor_position(),
105            },
106            title: screen.surface.title().to_owned(),
107            mode: screen.mode,
108        })
109    }
110}