crossterm/
command.rs

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}