1use std::io::{stdout, BufWriter, Stdout, Write};
2
3use crossterm::{
4 cursor::{Hide, MoveTo, MoveToColumn, MoveToNextLine, Show},
5 event::{
6 DisableMouseCapture,
7 EnableMouseCapture,
8 KeyboardEnhancementFlags,
9 PopKeyboardEnhancementFlags,
10 PushKeyboardEnhancementFlags,
11 },
12 style::{available_color_count, Attribute, Colors, Print, ResetColor, SetAttribute, SetColors},
13 terminal::{
14 disable_raw_mode,
15 enable_raw_mode,
16 size,
17 Clear,
18 ClearType,
19 DisableLineWrap,
20 EnableLineWrap,
21 EnterAlternateScreen,
22 LeaveAlternateScreen,
23 },
24 Command,
25 QueueableCommand,
26};
27
28use super::{color_mode::ColorMode, size::Size, tui::Tui, utils::detect_color_mode};
29use crate::error::DisplayError;
30
31#[derive(Debug)]
33pub struct CrossTerm {
34 color_mode: ColorMode,
35 window: BufWriter<Stdout>,
36}
37
38impl Tui for CrossTerm {
39 #[inline]
40 fn get_color_mode(&self) -> ColorMode {
41 self.color_mode
42 }
43
44 #[inline]
45 fn reset(&mut self) -> Result<(), DisplayError> {
46 self.queue_command(ResetColor)?;
47 self.queue_command(SetAttribute(Attribute::Reset))?;
48 self.queue_command(Clear(ClearType::All))?;
49 self.queue_command(MoveTo(0, 0))
50 }
51
52 #[inline]
53 fn flush(&mut self) -> Result<(), DisplayError> {
54 self.window.flush().map_err(DisplayError::Unexpected)
55 }
56
57 #[inline]
58 fn print(&mut self, s: &str) -> Result<(), DisplayError> {
59 self.queue_command(Print(s))
60 }
61
62 #[inline]
63 fn set_color(&mut self, colors: Colors) -> Result<(), DisplayError> {
64 self.queue_command(SetColors(colors))
65 }
66
67 #[inline]
68 fn set_dim(&mut self, dim: bool) -> Result<(), DisplayError> {
69 self.queue_command(SetAttribute(
70 if dim {
71 Attribute::Dim
72 }
73 else {
74 Attribute::NormalIntensity
75 },
76 ))
77 }
78
79 #[inline]
80 fn set_underline(&mut self, underline: bool) -> Result<(), DisplayError> {
81 self.queue_command(SetAttribute(
82 if underline {
83 Attribute::Underlined
84 }
85 else {
86 Attribute::NoUnderline
87 },
88 ))
89 }
90
91 #[inline]
92 fn set_reverse(&mut self, reverse: bool) -> Result<(), DisplayError> {
93 self.queue_command(SetAttribute(
94 if reverse {
95 Attribute::Reverse
96 }
97 else {
98 Attribute::NoReverse
99 },
100 ))
101 }
102
103 #[inline]
104 fn get_size(&self) -> Size {
105 size().map_or_else(
106 |_| Size::new(0, 0),
107 |(width, height)| Size::new(usize::from(width), usize::from(height)),
108 )
109 }
110
111 #[inline]
112 fn move_to_column(&mut self, x: u16) -> Result<(), DisplayError> {
113 self.queue_command(MoveToColumn(x))
114 }
115
116 #[inline]
117 fn move_next_line(&mut self) -> Result<(), DisplayError> {
118 self.queue_command(MoveToNextLine(1))
119 }
120
121 #[inline]
122 fn start(&mut self) -> Result<(), DisplayError> {
123 self.queue_command(EnterAlternateScreen)?;
124 self.queue_command(DisableLineWrap)?;
125 self.queue_command(Hide)?;
126 self.queue_command(EnableMouseCapture)?;
127 let _command_result = self.queue_command(PushKeyboardEnhancementFlags(
129 KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
130 | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
131 | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
132 | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
133 ));
134 enable_raw_mode().map_err(DisplayError::Unexpected)?;
135 self.flush()
136 }
137
138 #[inline]
139 fn end(&mut self) -> Result<(), DisplayError> {
140 let _command_result = self.queue_command(PopKeyboardEnhancementFlags);
142 self.queue_command(DisableMouseCapture)?;
143 self.queue_command(Show)?;
144 self.queue_command(EnableLineWrap)?;
145 self.queue_command(LeaveAlternateScreen)?;
146 disable_raw_mode().map_err(DisplayError::Unexpected)?;
147 self.flush()
148 }
149}
150
151impl CrossTerm {
152 #[inline]
154 #[must_use]
155 pub fn new() -> Self {
156 Self {
157 window: BufWriter::new(stdout()),
158 color_mode: detect_color_mode(available_color_count()),
159 }
160 }
161
162 fn queue_command(&mut self, command: impl Command) -> Result<(), DisplayError> {
163 let _result = self.window.queue(command).map_err(DisplayError::Unexpected)?;
164 Ok(())
165 }
166}