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}