1use std::fmt;
2use std::io::{self, Write};
3
4use crate::terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate};
5
6/// An interface for a command that performs an action on the terminal.
7///
8/// Crossterm provides a set of commands,
9/// and there is no immediate reason to implement a command yourself.
10/// In order to understand how to use and execute commands,
11/// it is recommended that you take a look at [Command API](./index.html#command-api) chapter.
12pub trait Command {
13 /// Write an ANSI representation of this command to the given writer.
14 /// An ANSI code can manipulate the terminal by writing it to the terminal buffer.
15 /// However, only Windows 10 and UNIX systems support this.
16 ///
17 /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api)
18 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result;
19
20 /// Execute this command.
21 ///
22 /// Windows versions lower than windows 10 do not support ANSI escape codes,
23 /// therefore a direct WinAPI call is made.
24 ///
25 /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api)
26 #[cfg(windows)]
27 fn execute_winapi(&self) -> io::Result<()>;
28
29 /// Returns whether the ANSI code representation of this command is supported by windows.
30 ///
31 /// A list of supported ANSI escape codes
32 /// can be found [here](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
33 #[cfg(windows)]
34 fn is_ansi_code_supported(&self) -> bool {
35 super::ansi_support::supports_ansi()
36 }
37}
38
39impl<T: Command + ?Sized> Command for &T {
40 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
41 (**self).write_ansi(f)
42 }
43
44 #[inline]
45 #[cfg(windows)]
46 fn execute_winapi(&self) -> io::Result<()> {
47 T::execute_winapi(self)
48 }
49
50 #[cfg(windows)]
51 #[inline]
52 fn is_ansi_code_supported(&self) -> bool {
53 T::is_ansi_code_supported(self)
54 }
55}
56
57/// An interface for types that can queue commands for further execution.
58pub trait QueueableCommand {
59 /// Queues the given command for further execution.
60 fn queue(&mut self, command: impl Command) -> io::Result<&mut Self>;
61}
62
63/// An interface for types that can directly execute commands.
64pub trait ExecutableCommand {
65 /// Executes the given command directly.
66 fn execute(&mut self, command: impl Command) -> io::Result<&mut Self>;
67}
68
69impl<T: Write + ?Sized> QueueableCommand for T {
70 /// Queues the given command for further execution.
71 ///
72 /// Queued commands will be executed in the following cases:
73 ///
74 /// * When `flush` is called manually on the given type implementing `io::Write`.
75 /// * The terminal will `flush` automatically if the buffer is full.
76 /// * Each line is flushed in case of `stdout`, because it is line buffered.
77 ///
78 /// # Arguments
79 ///
80 /// - [Command](./trait.Command.html)
81 ///
82 /// The command that you want to queue for later execution.
83 ///
84 /// # Examples
85 ///
86 /// ```rust
87 /// use std::io::{self, Write};
88 /// use crossterm::{QueueableCommand, style::Print};
89 ///
90 /// fn main() -> io::Result<()> {
91 /// let mut stdout = io::stdout();
92 ///
93 /// // `Print` will executed executed when `flush` is called.
94 /// stdout
95 /// .queue(Print("foo 1\n".to_string()))?
96 /// .queue(Print("foo 2".to_string()))?;
97 ///
98 /// // some other code (no execution happening here) ...
99 ///
100 /// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed.
101 /// stdout.flush()?;
102 ///
103 /// Ok(())
104 ///
105 /// // ==== Output ====
106 /// // foo 1
107 /// // foo 2
108 /// }
109 /// ```
110 ///
111 /// Have a look over at the [Command API](./index.html#command-api) for more details.
112 ///
113 /// # Notes
114 ///
115 /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
116 /// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
117 /// The reason for this is that Windows versions lower than 10 do not support ANSI codes,
118 /// and can therefore not be written to the given `writer`.
119 /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
120 /// and [queue](./trait.QueueableCommand.html) for those old Windows versions.
121 fn queue(&mut self, command: impl Command) -> io::Result<&mut Self> {
122 #[cfg(windows)]
123 if !command.is_ansi_code_supported() {
124 // There may be queued commands in this writer, but `execute_winapi` will execute the
125 // command immediately. To prevent commands being executed out of order we flush the
126 // writer now.
127 self.flush()?;
128 command.execute_winapi()?;
129 return Ok(self);
130 }
131
132 write_command_ansi(self, command)?;
133 Ok(self)
134 }
135}
136
137impl<T: Write + ?Sized> ExecutableCommand for T {
138 /// Executes the given command directly.
139 ///
140 /// The given command its ANSI escape code will be written and flushed onto `Self`.
141 ///
142 /// # Arguments
143 ///
144 /// - [Command](./trait.Command.html)
145 ///
146 /// The command that you want to execute directly.
147 ///
148 /// # Example
149 ///
150 /// ```rust
151 /// use std::io;
152 /// use crossterm::{ExecutableCommand, style::Print};
153 ///
154 /// fn main() -> io::Result<()> {
155 /// // will be executed directly
156 /// io::stdout()
157 /// .execute(Print("sum:\n".to_string()))?
158 /// .execute(Print(format!("1 + 1= {} ", 1 + 1)))?;
159 ///
160 /// Ok(())
161 ///
162 /// // ==== Output ====
163 /// // sum:
164 /// // 1 + 1 = 2
165 /// }
166 /// ```
167 ///
168 /// Have a look over at the [Command API](./index.html#command-api) for more details.
169 ///
170 /// # Notes
171 ///
172 /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
173 /// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
174 /// The reason for this is that Windows versions lower than 10 do not support ANSI codes,
175 /// and can therefore not be written to the given `writer`.
176 /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
177 /// and [queue](./trait.QueueableCommand.html) for those old Windows versions.
178 fn execute(&mut self, command: impl Command) -> io::Result<&mut Self> {
179 self.queue(command)?;
180 self.flush()?;
181 Ok(self)
182 }
183}
184
185/// An interface for types that support synchronized updates.
186pub trait SynchronizedUpdate {
187 /// Performs a set of actions against the given type.
188 fn sync_update<T>(&mut self, operations: impl FnOnce(&mut Self) -> T) -> io::Result<T>;
189}
190
191impl<W: std::io::Write + ?Sized> SynchronizedUpdate for W {
192 /// Performs a set of actions within a synchronous update.
193 ///
194 /// Updates will be suspended in the terminal, the function will be executed against self,
195 /// updates will be resumed, and a flush will be performed.
196 ///
197 /// # Arguments
198 ///
199 /// - Function
200 ///
201 /// A function that performs the operations that must execute in a synchronized update.
202 ///
203 /// # Examples
204 ///
205 /// ```rust
206 /// use std::io;
207 /// use crossterm::{ExecutableCommand, SynchronizedUpdate, style::Print};
208 ///
209 /// fn main() -> io::Result<()> {
210 /// let mut stdout = io::stdout();
211 ///
212 /// stdout.sync_update(|stdout| {
213 /// stdout.execute(Print("foo 1\n".to_string()))?;
214 /// stdout.execute(Print("foo 2".to_string()))?;
215 /// // The effects of the print command will not be present in the terminal
216 /// // buffer, but not visible in the terminal.
217 /// std::io::Result::Ok(())
218 /// })?;
219 ///
220 /// // The effects of the commands will be visible.
221 ///
222 /// Ok(())
223 ///
224 /// // ==== Output ====
225 /// // foo 1
226 /// // foo 2
227 /// }
228 /// ```
229 ///
230 /// # Notes
231 ///
232 /// This command is performed only using ANSI codes, and will do nothing on terminals that do not support ANSI
233 /// codes, or this specific extension.
234 ///
235 /// When rendering the screen of the terminal, the Emulator usually iterates through each visible grid cell and
236 /// renders its current state. With applications updating the screen a at higher frequency this can cause tearing.
237 ///
238 /// This mode attempts to mitigate that.
239 ///
240 /// When the synchronization mode is enabled following render calls will keep rendering the last rendered state.
241 /// The terminal Emulator keeps processing incoming text and sequences. When the synchronized update mode is disabled
242 /// again the renderer may fetch the latest screen buffer state again, effectively avoiding the tearing effect
243 /// by unintentionally rendering in the middle a of an application screen update.
244 ///
245 fn sync_update<T>(&mut self, operations: impl FnOnce(&mut Self) -> T) -> io::Result<T> {
246 self.queue(BeginSynchronizedUpdate)?;
247 let result = operations(self);
248 self.execute(EndSynchronizedUpdate)?;
249 Ok(result)
250 }
251}
252/// Writes the ANSI representation of a command to the given writer.
253fn write_command_ansi<C: Command>(
254 io: &mut (impl io::Write + ?Sized),
255 command: C,
256) -> io::Result<()> {
257 struct Adapter<T> {
258 inner: T,
259 res: io::Result<()>,
260 }
261
262 impl<T: Write> fmt::Write for Adapter<T> {
263 fn write_str(&mut self, s: &str) -> fmt::Result {
264 self.inner.write_all(s.as_bytes()).map_err(|e| {
265 self.res = Err(e);
266 fmt::Error
267 })
268 }
269 }
270
271 let mut adapter = Adapter {
272 inner: io,
273 res: Ok(()),
274 };
275
276 command
277 .write_ansi(&mut adapter)
278 .map_err(|fmt::Error| match adapter.res {
279 Ok(()) => panic!(
280 "<{}>::write_ansi incorrectly errored",
281 std::any::type_name::<C>()
282 ),
283 Err(e) => e,
284 })
285}
286
287/// Executes the ANSI representation of a command, using the given `fmt::Write`.
288pub(crate) fn execute_fmt(f: &mut impl fmt::Write, command: impl Command) -> fmt::Result {
289 #[cfg(windows)]
290 if !command.is_ansi_code_supported() {
291 return command.execute_winapi().map_err(|_| fmt::Error);
292 }
293
294 command.write_ansi(f)
295}
296