thedes_tui_core/screen/device/
native.rs

1use std::fmt::Write as _;
2
3use crossterm::{
4    Command as _,
5    cursor,
6    terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
7};
8use thedes_async_util::dyn_async_trait;
9use tokio::io::{self, AsyncWriteExt, Stdout};
10
11use crate::{color::native_ext::ColorToCrossterm, geometry::CoordPair};
12
13use super::{Command, Error, ScreenDevice};
14
15pub fn open() -> Box<dyn ScreenDevice> {
16    Box::new(NativeScreenDevice::new())
17}
18
19#[derive(Debug)]
20struct NativeScreenDevice {
21    buf: String,
22    target: Stdout,
23}
24
25impl NativeScreenDevice {
26    pub fn new() -> Self {
27        Self { buf: String::new(), target: io::stdout() }
28    }
29
30    fn write_command(&mut self, command: Command) -> Result<(), Error> {
31        match command {
32            Command::Enter => {
33                EnterAlternateScreen.write_ansi(&mut self.buf)?;
34            },
35
36            Command::Leave => {
37                LeaveAlternateScreen.write_ansi(&mut self.buf)?;
38            },
39
40            Command::Clear => {
41                write!(
42                    self.buf,
43                    "{}",
44                    terminal::Clear(terminal::ClearType::All)
45                )?;
46            },
47
48            Command::ResetBackground => {
49                write!(
50                    self.buf,
51                    "{}",
52                    crossterm::style::SetBackgroundColor(
53                        crossterm::style::Color::Reset
54                    )
55                )?;
56            },
57
58            Command::ResetForeground => {
59                write!(
60                    self.buf,
61                    "{}",
62                    crossterm::style::SetForegroundColor(
63                        crossterm::style::Color::Reset
64                    )
65                )?;
66            },
67
68            Command::SetBackground(color) => {
69                write!(
70                    self.buf,
71                    "{}",
72                    crossterm::style::SetBackgroundColor(color.to_crossterm()),
73                )?;
74            },
75
76            Command::SetForeground(color) => {
77                write!(
78                    self.buf,
79                    "{}",
80                    crossterm::style::SetForegroundColor(color.to_crossterm()),
81                )?;
82            },
83
84            Command::ShowCursor => {
85                write!(self.buf, "{}", cursor::Show)?;
86            },
87
88            Command::HideCursor => {
89                write!(self.buf, "{}", cursor::Hide)?;
90            },
91
92            Command::MoveCursor(point) => {
93                write!(self.buf, "{}", cursor::MoveTo(point.x, point.y))?;
94            },
95
96            Command::Write(ch) => {
97                self.buf.push(ch);
98            },
99        }
100
101        Ok(())
102    }
103}
104
105#[dyn_async_trait]
106impl ScreenDevice for NativeScreenDevice {
107    fn send_raw(
108        &mut self,
109        commands: &mut (dyn Iterator<Item = Command> + Send + Sync),
110    ) -> Result<(), Error> {
111        for command in commands {
112            self.write_command(command)?;
113        }
114        Ok(())
115    }
116
117    async fn flush(&mut self) -> Result<(), Error> {
118        if !self.buf.is_empty() {
119            self.target.write_all(self.buf.as_bytes()).await?;
120            self.target.flush().await?;
121            self.buf.clear();
122        }
123        Ok(())
124    }
125
126    fn blocking_get_size(&mut self) -> Result<CoordPair, Error> {
127        let (x, y) = terminal::size()?;
128        Ok(CoordPair { y, x })
129    }
130}