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