ratatui_core/
backend.rs

1#![warn(missing_docs)]
2//! This module provides the backend implementations for different terminal libraries.
3//!
4//! It defines the [`Backend`] trait which is used to abstract over the specific terminal library
5//! being used.
6//!
7//! Supported terminal backends:
8//! - [Crossterm]: enable the `crossterm` feature (enabled by default) and use [`CrosstermBackend`]
9//! - [Termion]: enable the `termion` feature and use [`TermionBackend`]
10//! - [Termwiz]: enable the `termwiz` feature and use [`TermwizBackend`]
11//!
12//! Additionally, a [`TestBackend`] is provided for testing purposes.
13//!
14//! See the [Backend Comparison] section of the [Ratatui Website] for more details on the different
15//! backends.
16//!
17//! Each backend supports a number of features, such as [raw mode](#raw-mode), [alternate
18//! screen](#alternate-screen), and [mouse capture](#mouse-capture). These features are generally
19//! not enabled by default, and must be enabled by the application before they can be used. See the
20//! documentation for each backend for more details.
21//!
22//! Note: most applications should use the [`Terminal`] struct instead of directly calling methods
23//! on the backend.
24//!
25//! # Example
26//!
27//! ```rust,ignore
28//! use std::io::stdout;
29//!
30//! use ratatui::{backend::CrosstermBackend, Terminal};
31//!
32//! let backend = CrosstermBackend::new(stdout());
33//! let mut terminal = Terminal::new(backend)?;
34//! terminal.clear()?;
35//! terminal.draw(|frame| {
36//!     // -- snip --
37//! })?;
38//! # std::io::Result::Ok(())
39//! ```
40//!
41//! See the the [Examples] directory for more examples.
42//!
43//! # Raw Mode
44//!
45//! Raw mode is a mode where the terminal does not perform any processing or handling of the input
46//! and output. This means that features such as echoing input characters, line buffering, and
47//! special character processing (e.g., CTRL-C for SIGINT) are disabled. This is useful for
48//! applications that want to have complete control over the terminal input and output, processing
49//! each keystroke themselves.
50//!
51//! For example, in raw mode, the terminal will not perform line buffering on the input, so the
52//! application will receive each key press as it is typed, instead of waiting for the user to
53//! press enter. This makes it suitable for real-time applications like text editors,
54//! terminal-based games, and more.
55//!
56//! Each backend handles raw mode differently, so the behavior may vary depending on the backend
57//! being used. Be sure to consult the backend's specific documentation for exact details on how it
58//! implements raw mode.
59//!
60//! # Alternate Screen
61//!
62//! The alternate screen is a separate buffer that some terminals provide, distinct from the main
63//! screen. When activated, the terminal will display the alternate screen, hiding the current
64//! content of the main screen. Applications can write to this screen as if it were the regular
65//! terminal display, but when the application exits, the terminal will switch back to the main
66//! screen, and the contents of the alternate screen will be cleared. This is useful for
67//! applications like text editors or terminal games that want to use the full terminal window
68//! without disrupting the command line or other terminal content.
69//!
70//! This creates a seamless transition between the application and the regular terminal session, as
71//! the content displayed before launching the application will reappear after the application
72//! exits.
73//!
74//! Note that not all terminal emulators support the alternate screen, and even those that do may
75//! handle it differently. As a result, the behavior may vary depending on the backend being used.
76//! Always consult the specific backend's documentation to understand how it implements the
77//! alternate screen.
78//!
79//! # Mouse Capture
80//!
81//! Mouse capture is a mode where the terminal captures mouse events such as clicks, scrolls, and
82//! movement, and sends them to the application as special sequences or events. This enables the
83//! application to handle and respond to mouse actions, providing a more interactive and graphical
84//! user experience within the terminal. It's particularly useful for applications like
85//! terminal-based games, text editors, or other programs that require more direct interaction from
86//! the user.
87//!
88//! Each backend handles mouse capture differently, with variations in the types of events that can
89//! be captured and how they are represented. As such, the behavior may vary depending on the
90//! backend being used, and developers should consult the specific backend's documentation to
91//! understand how it implements mouse capture.
92//!
93//! [`CrosstermBackend`]: https://docs.rs/ratatui/latest/ratatui/backend/struct.CrosstermBackend.html
94//! [`TermionBackend`]: https://docs.rs/ratatui/latest/ratatui/backend/struct.TermionBackend.html
95//! [`TermwizBackend`]: https://docs.rs/ratatui/latest/ratatui/backend/struct.TermwizBackend.html
96//! [`Terminal`]: https://docs.rs/ratatui/latest/ratatui/struct.Terminal.html
97//! [Crossterm]: https://crates.io/crates/crossterm
98//! [Termion]: https://crates.io/crates/termion
99//! [Termwiz]: https://crates.io/crates/termwiz
100//! [Examples]: https://github.com/ratatui/ratatui/tree/main/ratatui/examples/README.md
101//! [Backend Comparison]: https://ratatui.rs/concepts/backends/comparison/
102//! [Ratatui Website]: https://ratatui.rs
103
104use strum::{Display, EnumString};
105
106use crate::buffer::Cell;
107use crate::layout::{Position, Size};
108
109mod test;
110pub use self::test::TestBackend;
111
112/// Enum representing the different types of clearing operations that can be performed
113/// on the terminal screen.
114#[derive(Debug, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
115pub enum ClearType {
116    /// Clear the entire screen.
117    All,
118    /// Clear everything after the cursor.
119    AfterCursor,
120    /// Clear everything before the cursor.
121    BeforeCursor,
122    /// Clear the current line.
123    CurrentLine,
124    /// Clear everything from the cursor until the next newline.
125    UntilNewLine,
126}
127
128/// The window size in characters (columns / rows) as well as pixels.
129#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
130pub struct WindowSize {
131    /// Size of the window in characters (columns / rows).
132    pub columns_rows: Size,
133    /// Size of the window in pixels.
134    ///
135    /// The `pixels` fields may not be implemented by all terminals and return `0,0`. See
136    /// <https://man7.org/linux/man-pages/man4/tty_ioctl.4.html> under section "Get and set window
137    /// size" / TIOCGWINSZ where the fields are commented as "unused".
138    pub pixels: Size,
139}
140
141/// The `Backend` trait provides an abstraction over different terminal libraries. It defines the
142/// methods required to draw content, manipulate the cursor, and clear the terminal screen.
143///
144/// Most applications should not need to interact with the `Backend` trait directly as the
145/// [`Terminal`] struct provides a higher level interface for interacting with the terminal.
146///
147/// [`Terminal`]: https://docs.rs/ratatui/latest/ratatui/struct.Terminal.html
148pub trait Backend {
149    /// Error type associated with this Backend.
150    type Error: core::error::Error;
151
152    /// Draw the given content to the terminal screen.
153    ///
154    /// The content is provided as an iterator over `(u16, u16, &Cell)` tuples, where the first two
155    /// elements represent the x and y coordinates, and the third element is a reference to the
156    /// [`Cell`] to be drawn.
157    fn draw<'a, I>(&mut self, content: I) -> Result<(), Self::Error>
158    where
159        I: Iterator<Item = (u16, u16, &'a Cell)>;
160
161    /// Insert `n` line breaks to the terminal screen.
162    ///
163    /// This method is optional and may not be implemented by all backends.
164    fn append_lines(&mut self, _n: u16) -> Result<(), Self::Error> {
165        Ok(())
166    }
167
168    /// Hide the cursor on the terminal screen.
169    ///
170    ///
171    /// See also [`show_cursor`].
172    /// # Example
173    ///
174    /// ```rust,ignore
175    /// # use ratatui::backend::{TestBackend};
176    /// # let mut backend = TestBackend::new(80, 25);
177    /// use ratatui::backend::Backend;
178    ///
179    /// backend.hide_cursor()?;
180    /// // do something with hidden cursor
181    /// backend.show_cursor()?;
182    /// # std::io::Result::Ok(())
183    /// ```
184    ///
185    /// [`show_cursor`]: Self::show_cursor
186    fn hide_cursor(&mut self) -> Result<(), Self::Error>;
187
188    /// Show the cursor on the terminal screen.
189    ///
190    /// See [`hide_cursor`] for an example.
191    ///
192    /// [`hide_cursor`]: Self::hide_cursor
193    fn show_cursor(&mut self) -> Result<(), Self::Error>;
194
195    /// Get the current cursor position on the terminal screen.
196    ///
197    /// The returned tuple contains the x and y coordinates of the cursor.
198    /// The origin (0, 0) is at the top left corner of the screen.
199    ///
200    /// See [`set_cursor_position`] for an example.
201    ///
202    /// [`set_cursor_position`]: Self::set_cursor_position
203    fn get_cursor_position(&mut self) -> Result<Position, Self::Error>;
204
205    /// Set the cursor position on the terminal screen to the given x and y coordinates.
206    ///
207    /// The origin (0, 0) is at the top left corner of the screen.
208    ///
209    /// # Example
210    ///
211    /// ```rust,ignore
212    /// # use ratatui::backend::{TestBackend};
213    /// # let mut backend = TestBackend::new(80, 25);
214    /// use ratatui::{backend::Backend, layout::Position};
215    ///
216    /// backend.set_cursor_position(Position { x: 10, y: 20 })?;
217    /// assert_eq!(backend.get_cursor_position()?, Position { x: 10, y: 20 });
218    /// # std::io::Result::Ok(())
219    /// ```
220    fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> Result<(), Self::Error>;
221
222    /// Get the current cursor position on the terminal screen.
223    ///
224    /// The returned tuple contains the x and y coordinates of the cursor. The origin
225    /// (0, 0) is at the top left corner of the screen.
226    #[deprecated = "use `get_cursor_position()` instead which returns `Result<Position>`"]
227    fn get_cursor(&mut self) -> Result<(u16, u16), Self::Error> {
228        let Position { x, y } = self.get_cursor_position()?;
229        Ok((x, y))
230    }
231
232    /// Set the cursor position on the terminal screen to the given x and y coordinates.
233    ///
234    /// The origin (0, 0) is at the top left corner of the screen.
235    #[deprecated = "use `set_cursor_position((x, y))` instead which takes `impl Into<Position>`"]
236    fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Self::Error> {
237        self.set_cursor_position(Position { x, y })
238    }
239
240    /// Clears the whole terminal screen
241    ///
242    /// # Example
243    ///
244    /// ```rust,ignore
245    /// # use ratatui::backend::{TestBackend};
246    /// # let mut backend = TestBackend::new(80, 25);
247    /// use ratatui::backend::Backend;
248    ///
249    /// backend.clear()?;
250    /// # std::io::Result::Ok(())
251    /// ```
252    fn clear(&mut self) -> Result<(), Self::Error>;
253
254    /// Clears a specific region of the terminal specified by the [`ClearType`] parameter
255    ///
256    /// This method is optional and may not be implemented by all backends. The default
257    /// implementation calls [`clear`] if the `clear_type` is [`ClearType::All`] and returns an
258    /// error otherwise.
259    ///
260    /// # Example
261    ///
262    /// ```rust,ignore
263    /// # use ratatui::{backend::{TestBackend}};
264    /// # let mut backend = TestBackend::new(80, 25);
265    /// use ratatui::backend::{Backend, ClearType};
266    ///
267    /// backend.clear_region(ClearType::All)?;
268    /// # std::io::Result::Ok(())
269    /// ```
270    ///
271    /// # Errors
272    ///
273    /// This method will return an error if the terminal screen could not be cleared. It will also
274    /// return an error if the `clear_type` is not supported by the backend.
275    ///
276    /// [`clear`]: Self::clear
277    fn clear_region(&mut self, clear_type: ClearType) -> Result<(), Self::Error>;
278
279    /// Get the size of the terminal screen in columns/rows as a [`Size`].
280    ///
281    /// The returned [`Size`] contains the width and height of the terminal screen.
282    ///
283    /// # Example
284    ///
285    /// ```rust,ignore
286    /// # use ratatui::{backend::{TestBackend}};
287    /// # let backend = TestBackend::new(80, 25);
288    /// use ratatui::{backend::Backend, layout::Size};
289    ///
290    /// assert_eq!(backend.size()?, Size::new(80, 25));
291    /// # Result::Ok(())
292    /// ```
293    fn size(&self) -> Result<Size, Self::Error>;
294
295    /// Get the size of the terminal screen in columns/rows and pixels as a [`WindowSize`].
296    ///
297    /// The reason for this not returning only the pixel size, given the redundancy with the
298    /// `size()` method, is that the underlying backends most likely get both values with one
299    /// syscall, and the user is also most likely to need columns and rows along with pixel size.
300    fn window_size(&mut self) -> Result<WindowSize, Self::Error>;
301
302    /// Flush any buffered content to the terminal screen.
303    fn flush(&mut self) -> Result<(), Self::Error>;
304
305    /// Scroll a region of the screen upwards, where a region is specified by a (half-open) range
306    /// of rows.
307    ///
308    /// Each row in the region is replaced by the row `line_count` rows below it, except the bottom
309    /// `line_count` rows, which are replaced by empty rows. If `line_count` is equal to or larger
310    /// than the number of rows in the region, then all rows are replaced with empty rows.
311    ///
312    /// If the region includes row 0, then `line_count` rows are copied into the bottom of the
313    /// scrollback buffer. These rows are first taken from the old contents of the region, starting
314    /// from the top. If there aren't sufficient rows in the region, then the remainder are empty
315    /// rows.
316    ///
317    /// The position of the cursor afterwards is undefined.
318    ///
319    /// The behavior is designed to match what ANSI terminals do when scrolling regions are
320    /// established. With ANSI terminals, a scrolling region can be established with the "^[[X;Yr"
321    /// sequence, where X and Y define the lines of the region. The scrolling region can be reset
322    /// to be the whole screen with the "^[[r" sequence.
323    ///
324    /// When a scrolling region is established in an ANSI terminal, various operations' behaviors
325    /// are changed in such a way that the scrolling region acts like a "virtual screen". In
326    /// particular, the scrolling sequence "^[[NS", which scrolls lines up by a count of N.
327    ///
328    /// On an ANSI terminal, this method will probably translate to something like:
329    /// "^[[X;Yr^[[NS^[[r". That is, set the scrolling region, scroll up, then reset the scrolling
330    /// region.
331    ///
332    /// For examples of how this function is expected to work, refer to the tests for
333    /// [`TestBackend::scroll_region_up`].
334    #[cfg(feature = "scrolling-regions")]
335    fn scroll_region_up(
336        &mut self,
337        region: core::ops::Range<u16>,
338        line_count: u16,
339    ) -> Result<(), Self::Error>;
340
341    /// Scroll a region of the screen downwards, where a region is specified by a (half-open) range
342    /// of rows.
343    ///
344    /// Each row in the region is replaced by the row `line_count` rows above it, except the top
345    /// `line_count` rows, which are replaced by empty rows. If `line_count` is equal to or larger
346    /// than the number of rows in the region, then all rows are replaced with empty rows.
347    ///
348    /// The position of the cursor afterwards is undefined.
349    ///
350    /// See the documentation for [`Self::scroll_region_down`] for more information about how this
351    /// is expected to be implemented for ANSI terminals. All of that applies, except the ANSI
352    /// sequence to scroll down is "^[[NT".
353    ///
354    /// This function is asymmetrical with regards to the scrollback buffer. The reason is that
355    /// this how terminals seem to implement things.
356    ///
357    /// For examples of how this function is expected to work, refer to the tests for
358    /// [`TestBackend::scroll_region_down`].
359    #[cfg(feature = "scrolling-regions")]
360    fn scroll_region_down(
361        &mut self,
362        region: core::ops::Range<u16>,
363        line_count: u16,
364    ) -> Result<(), Self::Error>;
365}
366
367#[cfg(test)]
368mod tests {
369    use alloc::string::ToString;
370
371    use strum::ParseError;
372
373    use super::*;
374
375    #[test]
376    fn clear_type_tostring() {
377        assert_eq!(ClearType::All.to_string(), "All");
378        assert_eq!(ClearType::AfterCursor.to_string(), "AfterCursor");
379        assert_eq!(ClearType::BeforeCursor.to_string(), "BeforeCursor");
380        assert_eq!(ClearType::CurrentLine.to_string(), "CurrentLine");
381        assert_eq!(ClearType::UntilNewLine.to_string(), "UntilNewLine");
382    }
383
384    #[test]
385    fn clear_type_from_str() {
386        assert_eq!("All".parse::<ClearType>(), Ok(ClearType::All));
387        assert_eq!(
388            "AfterCursor".parse::<ClearType>(),
389            Ok(ClearType::AfterCursor)
390        );
391        assert_eq!(
392            "BeforeCursor".parse::<ClearType>(),
393            Ok(ClearType::BeforeCursor)
394        );
395        assert_eq!(
396            "CurrentLine".parse::<ClearType>(),
397            Ok(ClearType::CurrentLine)
398        );
399        assert_eq!(
400            "UntilNewLine".parse::<ClearType>(),
401            Ok(ClearType::UntilNewLine)
402        );
403        assert_eq!("".parse::<ClearType>(), Err(ParseError::VariantNotFound));
404    }
405}