1//! # Terminal
2//!
3//! The `terminal` module provides functionality to work with the terminal.
4//!
5//! This documentation does not contain a lot of examples. The reason is that it's fairly
6//! obvious how to use this crate. Although, we do provide
7//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository
8//! to demonstrate the capabilities.
9//!
10//! Most terminal actions can be performed with commands.
11//! Please have a look at [command documentation](../index.html#command-api) for a more detailed documentation.
12//!
13//! ## Screen Buffer
14//!
15//! A screen buffer is a two-dimensional array of character
16//! and color data which is displayed in a terminal screen.
17//!
18//! The terminal has several of those buffers and is able to switch between them.
19//! The default screen in which you work is called the 'main screen'.
20//! The other screens are called the 'alternative screen'.
21//!
22//! It is important to understand that crossterm does not yet support creating screens,
23//! or switch between more than two buffers, and only offers the ability to change
24//! between the 'alternate' and 'main screen'.
25//!
26//! ### Alternate Screen
27//!
28//! By default, you will be working on the main screen.
29//! There is also another screen called the 'alternative' screen.
30//! This screen is slightly different from the main screen.
31//! For example, it has the exact dimensions of the terminal window,
32//! without any scroll-back area.
33//!
34//! Crossterm offers the possibility to switch to the 'alternative' screen,
35//! make some modifications, and move back to the 'main' screen again.
36//! The main screen will stay intact and will have the original data as we performed all
37//! operations on the alternative screen.
38//!
39//! An good example of this is Vim.
40//! When it is launched from bash, a whole new buffer is used to modify a file.
41//! Then, when the modification is finished, it closes again and continues on the main screen.
42//!
43//! ### Raw Mode
44//!
45//! By default, the terminal functions in a certain way.
46//! For example, it will move the cursor to the beginning of the next line when the input hits the end of a line.
47//! Or that the backspace is interpreted for character removal.
48//!
49//! Sometimes these default modes are irrelevant,
50//! and in this case, we can turn them off.
51//! This is what happens when you enable raw modes.
52//!
53//! Those modes will be set when enabling raw modes:
54//!
55//! - Input will not be forwarded to screen
56//! - Input will not be processed on enter press
57//! - Input will not be line buffered (input sent byte-by-byte to input buffer)
58//! - Special keys like backspace and CTRL+C will not be processed by terminal driver
59//! - New line character will not be processed therefore `println!` can't be used, use `write!` instead
60//!
61//! Raw mode can be enabled/disabled with the [enable_raw_mode](terminal::enable_raw_mode) and [disable_raw_mode](terminal::disable_raw_mode) functions.
62//!
63//! ## Examples
64//!
65//! ```no_run
66//! use std::io::{self, Write};
67//! use crossterm::{execute, terminal::{ScrollUp, SetSize, size}};
68//!
69//! fn main() -> io::Result<()> {
70//! let (cols, rows) = size()?;
71//! // Resize terminal and scroll up.
72//! execute!(
73//! io::stdout(),
74//! SetSize(10, 10),
75//! ScrollUp(5)
76//! )?;
77//!
78//! // Be a good citizen, cleanup
79//! execute!(io::stdout(), SetSize(cols, rows))?;
80//! Ok(())
81//! }
82//! ```
83//!
84//! For manual execution control check out [crossterm::queue](../macro.queue.html).
85
86use std::{fmt, io};
87
88#[cfg(windows)]
89use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer};
90#[cfg(feature = "serde")]
91use serde::{Deserialize, Serialize};
92#[cfg(windows)]
93use winapi::um::wincon::ENABLE_WRAP_AT_EOL_OUTPUT;
94
95#[doc(no_inline)]
96use crate::Command;
97use crate::{csi, impl_display};
98
99pub(crate) mod sys;
100
101#[cfg(feature = "events")]
102pub use sys::supports_keyboard_enhancement;
103
104/// Tells whether the raw mode is enabled.
105///
106/// Please have a look at the [raw mode](./index.html#raw-mode) section.
107pub fn is_raw_mode_enabled() -> io::Result<bool> {
108 #[cfg(unix)]
109 {
110 Ok(sys::is_raw_mode_enabled())
111 }
112
113 #[cfg(windows)]
114 {
115 sys::is_raw_mode_enabled()
116 }
117}
118
119/// Enables raw mode.
120///
121/// Please have a look at the [raw mode](./index.html#raw-mode) section.
122pub fn enable_raw_mode() -> io::Result<()> {
123 sys::enable_raw_mode()
124}
125
126/// Disables raw mode.
127///
128/// Please have a look at the [raw mode](./index.html#raw-mode) section.
129pub fn disable_raw_mode() -> io::Result<()> {
130 sys::disable_raw_mode()
131}
132
133/// Returns the terminal size `(columns, rows)`.
134///
135/// The top left cell is represented `(1, 1)`.
136pub fn size() -> io::Result<(u16, u16)> {
137 sys::size()
138}
139
140#[derive(Debug)]
141pub struct WindowSize {
142 pub rows: u16,
143 pub columns: u16,
144 pub width: u16,
145 pub height: u16,
146}
147
148/// Returns the terminal size `[WindowSize]`.
149///
150/// The width and height in pixels may not be reliably implemented or default to 0.
151/// For unix, https://man7.org/linux/man-pages/man4/tty_ioctl.4.html documents them as "unused".
152/// For windows it is not implemented.
153pub fn window_size() -> io::Result<WindowSize> {
154 sys::window_size()
155}
156
157/// Disables line wrapping.
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct DisableLineWrap;
160
161impl Command for DisableLineWrap {
162 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
163 f.write_str(csi!("?7l"))
164 }
165
166 #[cfg(windows)]
167 fn execute_winapi(&self) -> io::Result<()> {
168 let screen_buffer = ScreenBuffer::current()?;
169 let console_mode = ConsoleMode::from(screen_buffer.handle().clone());
170 let new_mode = console_mode.mode()? & !ENABLE_WRAP_AT_EOL_OUTPUT;
171 console_mode.set_mode(new_mode)?;
172 Ok(())
173 }
174}
175
176/// Enable line wrapping.
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178pub struct EnableLineWrap;
179
180impl Command for EnableLineWrap {
181 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
182 f.write_str(csi!("?7h"))
183 }
184
185 #[cfg(windows)]
186 fn execute_winapi(&self) -> io::Result<()> {
187 let screen_buffer = ScreenBuffer::current()?;
188 let console_mode = ConsoleMode::from(screen_buffer.handle().clone());
189 let new_mode = console_mode.mode()? | ENABLE_WRAP_AT_EOL_OUTPUT;
190 console_mode.set_mode(new_mode)?;
191 Ok(())
192 }
193}
194
195/// A command that switches to alternate screen.
196///
197/// # Notes
198///
199/// * Commands must be executed/queued for execution otherwise they do nothing.
200/// * Use [LeaveAlternateScreen](./struct.LeaveAlternateScreen.html) command to leave the entered alternate screen.
201///
202/// # Examples
203///
204/// ```no_run
205/// use std::io::{self, Write};
206/// use crossterm::{execute, terminal::{EnterAlternateScreen, LeaveAlternateScreen}};
207///
208/// fn main() -> io::Result<()> {
209/// execute!(io::stdout(), EnterAlternateScreen)?;
210///
211/// // Do anything on the alternate screen
212///
213/// execute!(io::stdout(), LeaveAlternateScreen)
214/// }
215/// ```
216///
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub struct EnterAlternateScreen;
219
220impl Command for EnterAlternateScreen {
221 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
222 f.write_str(csi!("?1049h"))
223 }
224
225 #[cfg(windows)]
226 fn execute_winapi(&self) -> io::Result<()> {
227 let alternate_screen = ScreenBuffer::create()?;
228 alternate_screen.show()?;
229 Ok(())
230 }
231}
232
233/// A command that switches back to the main screen.
234///
235/// # Notes
236///
237/// * Commands must be executed/queued for execution otherwise they do nothing.
238/// * Use [EnterAlternateScreen](./struct.EnterAlternateScreen.html) to enter the alternate screen.
239///
240/// # Examples
241///
242/// ```no_run
243/// use std::io::{self, Write};
244/// use crossterm::{execute, terminal::{EnterAlternateScreen, LeaveAlternateScreen}};
245///
246/// fn main() -> io::Result<()> {
247/// execute!(io::stdout(), EnterAlternateScreen)?;
248///
249/// // Do anything on the alternate screen
250///
251/// execute!(io::stdout(), LeaveAlternateScreen)
252/// }
253/// ```
254///
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub struct LeaveAlternateScreen;
257
258impl Command for LeaveAlternateScreen {
259 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
260 f.write_str(csi!("?1049l"))
261 }
262
263 #[cfg(windows)]
264 fn execute_winapi(&self) -> io::Result<()> {
265 let screen_buffer = ScreenBuffer::from(Handle::current_out_handle()?);
266 screen_buffer.show()?;
267 Ok(())
268 }
269}
270
271/// Different ways to clear the terminal buffer.
272#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
273#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
274pub enum ClearType {
275 /// All cells.
276 All,
277 /// All plus history
278 Purge,
279 /// All cells from the cursor position downwards.
280 FromCursorDown,
281 /// All cells from the cursor position upwards.
282 FromCursorUp,
283 /// All cells at the cursor row.
284 CurrentLine,
285 /// All cells from the cursor position until the new line.
286 UntilNewLine,
287}
288
289/// A command that scrolls the terminal screen a given number of rows up.
290///
291/// # Notes
292///
293/// Commands must be executed/queued for execution otherwise they do nothing.
294#[derive(Debug, Clone, Copy, PartialEq, Eq)]
295pub struct ScrollUp(pub u16);
296
297impl Command for ScrollUp {
298 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
299 if self.0 != 0 {
300 write!(f, csi!("{}S"), self.0)?;
301 }
302 Ok(())
303 }
304
305 #[cfg(windows)]
306 fn execute_winapi(&self) -> io::Result<()> {
307 sys::scroll_up(self.0)
308 }
309}
310
311/// A command that scrolls the terminal screen a given number of rows down.
312///
313/// # Notes
314///
315/// Commands must be executed/queued for execution otherwise they do nothing.
316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub struct ScrollDown(pub u16);
318
319impl Command for ScrollDown {
320 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
321 if self.0 != 0 {
322 write!(f, csi!("{}T"), self.0)?;
323 }
324 Ok(())
325 }
326
327 #[cfg(windows)]
328 fn execute_winapi(&self) -> io::Result<()> {
329 sys::scroll_down(self.0)
330 }
331}
332
333/// A command that clears the terminal screen buffer.
334///
335/// See the [`ClearType`](enum.ClearType.html) enum.
336///
337/// # Notes
338///
339/// Commands must be executed/queued for execution otherwise they do nothing.
340#[derive(Debug, Clone, Copy, PartialEq, Eq)]
341pub struct Clear(pub ClearType);
342
343impl Command for Clear {
344 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
345 f.write_str(match self.0 {
346 ClearType::All => csi!("2J"),
347 ClearType::Purge => csi!("3J"),
348 ClearType::FromCursorDown => csi!("J"),
349 ClearType::FromCursorUp => csi!("1J"),
350 ClearType::CurrentLine => csi!("2K"),
351 ClearType::UntilNewLine => csi!("K"),
352 })
353 }
354
355 #[cfg(windows)]
356 fn execute_winapi(&self) -> io::Result<()> {
357 sys::clear(self.0)
358 }
359}
360
361/// A command that sets the terminal buffer size `(columns, rows)`.
362///
363/// # Notes
364///
365/// Commands must be executed/queued for execution otherwise they do nothing.
366#[derive(Debug, Clone, Copy, PartialEq, Eq)]
367pub struct SetSize(pub u16, pub u16);
368
369impl Command for SetSize {
370 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
371 write!(f, csi!("8;{};{}t"), self.1, self.0)
372 }
373
374 #[cfg(windows)]
375 fn execute_winapi(&self) -> io::Result<()> {
376 sys::set_size(self.0, self.1)
377 }
378}
379
380/// A command that sets the terminal title
381///
382/// # Notes
383///
384/// Commands must be executed/queued for execution otherwise they do nothing.
385#[derive(Debug, Clone, Copy, PartialEq, Eq)]
386pub struct SetTitle<T>(pub T);
387
388impl<T: fmt::Display> Command for SetTitle<T> {
389 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
390 write!(f, "\x1B]0;{}\x07", &self.0)
391 }
392
393 #[cfg(windows)]
394 fn execute_winapi(&self) -> io::Result<()> {
395 sys::set_window_title(&self.0)
396 }
397}
398
399/// A command that instructs the terminal emulator to begin a synchronized frame.
400///
401/// # Notes
402///
403/// * Commands must be executed/queued for execution otherwise they do nothing.
404/// * Use [EndSynchronizedUpdate](./struct.EndSynchronizedUpdate.html) command to leave the entered alternate screen.
405///
406/// When rendering the screen of the terminal, the Emulator usually iterates through each visible grid cell and
407/// renders its current state. With applications updating the screen a at higher frequency this can cause tearing.
408///
409/// This mode attempts to mitigate that.
410///
411/// When the synchronization mode is enabled following render calls will keep rendering the last rendered state.
412/// The terminal Emulator keeps processing incoming text and sequences. When the synchronized update mode is disabled
413/// again the renderer may fetch the latest screen buffer state again, effectively avoiding the tearing effect
414/// by unintentionally rendering in the middle a of an application screen update.
415///
416/// # Examples
417///
418/// ```no_run
419/// use std::io::{self, Write};
420/// use crossterm::{execute, terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate}};
421///
422/// fn main() -> io::Result<()> {
423/// execute!(io::stdout(), BeginSynchronizedUpdate)?;
424///
425/// // Anything performed here will not be rendered until EndSynchronizedUpdate is called.
426///
427/// execute!(io::stdout(), EndSynchronizedUpdate)?;
428/// Ok(())
429/// }
430/// ```
431///
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub struct BeginSynchronizedUpdate;
434
435impl Command for BeginSynchronizedUpdate {
436 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
437 f.write_str(csi!("?2026h"))
438 }
439
440 #[cfg(windows)]
441 fn execute_winapi(&self) -> io::Result<()> {
442 Ok(())
443 }
444
445 #[cfg(windows)]
446 #[inline]
447 fn is_ansi_code_supported(&self) -> bool {
448 true
449 }
450}
451
452/// A command that instructs the terminal to end a synchronized frame.
453///
454/// # Notes
455///
456/// * Commands must be executed/queued for execution otherwise they do nothing.
457/// * Use [BeginSynchronizedUpdate](./struct.BeginSynchronizedUpdate.html) to enter the alternate screen.
458///
459/// When rendering the screen of the terminal, the Emulator usually iterates through each visible grid cell and
460/// renders its current state. With applications updating the screen a at higher frequency this can cause tearing.
461///
462/// This mode attempts to mitigate that.
463///
464/// When the synchronization mode is enabled following render calls will keep rendering the last rendered state.
465/// The terminal Emulator keeps processing incoming text and sequences. When the synchronized update mode is disabled
466/// again the renderer may fetch the latest screen buffer state again, effectively avoiding the tearing effect
467/// by unintentionally rendering in the middle a of an application screen update.
468///
469/// # Examples
470///
471/// ```no_run
472/// use std::io::{self, Write};
473/// use crossterm::{execute, terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate}};
474///
475/// fn main() -> io::Result<()> {
476/// execute!(io::stdout(), BeginSynchronizedUpdate)?;
477///
478/// // Anything performed here will not be rendered until EndSynchronizedUpdate is called.
479///
480/// execute!(io::stdout(), EndSynchronizedUpdate)?;
481/// Ok(())
482/// }
483/// ```
484///
485#[derive(Debug, Clone, Copy, PartialEq, Eq)]
486pub struct EndSynchronizedUpdate;
487
488impl Command for EndSynchronizedUpdate {
489 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
490 f.write_str(csi!("?2026l"))
491 }
492
493 #[cfg(windows)]
494 fn execute_winapi(&self) -> io::Result<()> {
495 Ok(())
496 }
497
498 #[cfg(windows)]
499 #[inline]
500 fn is_ansi_code_supported(&self) -> bool {
501 true
502 }
503}
504
505impl_display!(for ScrollUp);
506impl_display!(for ScrollDown);
507impl_display!(for SetSize);
508impl_display!(for Clear);
509
510#[cfg(test)]
511mod tests {
512 use std::{io::stdout, thread, time};
513
514 use crate::execute;
515
516 use super::*;
517
518 // Test is disabled, because it's failing on Travis CI
519 #[test]
520 #[ignore]
521 fn test_resize_ansi() {
522 let (width, height) = size().unwrap();
523
524 execute!(stdout(), SetSize(35, 35)).unwrap();
525
526 // see issue: https://github.com/eminence/terminal-size/issues/11
527 thread::sleep(time::Duration::from_millis(30));
528
529 assert_eq!((35, 35), size().unwrap());
530
531 // reset to previous size
532 execute!(stdout(), SetSize(width, height)).unwrap();
533
534 // see issue: https://github.com/eminence/terminal-size/issues/11
535 thread::sleep(time::Duration::from_millis(30));
536
537 assert_eq!((width, height), size().unwrap());
538 }
539
540 #[test]
541 fn test_raw_mode() {
542 // check we start from normal mode (may fail on some test harnesses)
543 assert!(!is_raw_mode_enabled().unwrap());
544
545 // enable the raw mode
546 if enable_raw_mode().is_err() {
547 // Enabling raw mode doesn't work on the ci
548 // So we just ignore it
549 return;
550 }
551
552 // check it worked (on unix it doesn't really check the underlying
553 // tty but rather check that the code is consistent)
554 assert!(is_raw_mode_enabled().unwrap());
555
556 // enable it again, this should not change anything
557 enable_raw_mode().unwrap();
558
559 // check we're still in raw mode
560 assert!(is_raw_mode_enabled().unwrap());
561
562 // now let's disable it
563 disable_raw_mode().unwrap();
564
565 // check we're back to normal mode
566 assert!(!is_raw_mode_enabled().unwrap());
567 }
568}
569