thedes_tui_core/screen/device/
native.rs1use 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}