crossterm/
terminal.rs

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}