Skip to main content

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