ratatui/
init.rs

1//! Terminal initialization and restoration functions.
2//!
3//! This module provides a set of convenience functions for initializing and restoring terminal
4//! state when creating Ratatui applications. These functions handle the common setup and teardown
5//! tasks required for terminal user interfaces.
6//!
7//! All functions in this module use the [`CrosstermBackend`] by default, which provides excellent
8//! cross-platform compatibility and is the recommended backend for most applications. The
9//! [`DefaultTerminal`] type alias encapsulates this choice, providing a ready-to-use terminal
10//! configuration that works well across different operating systems. For more information about
11//! backend choices and alternatives, see the [`backend`](`crate::backend`) module.
12//!
13//! Once you have initialized a terminal using the functions in this module, you can use it to
14//! [draw the UI](`crate#drawing-the-ui`) and [handle events](`crate#handling-events`). For more
15//! information about building widgets for your application, see the [`widgets`](`crate::widgets`)
16//! module.
17//!
18//! **Note**: All functions and types in this module are re-exported at the crate root for
19//! convenience, so you can call `ratatui::run()`, `ratatui::init()`, etc. instead of
20//! `ratatui::init::run()`, `ratatui::init::init()`, etc.
21//!
22//! # Available Types and Functions
23//!
24//! ## Types
25//!
26//! - [`DefaultTerminal`] - A type alias for `Terminal<CrosstermBackend<Stdout>>`, providing a
27//!   reasonable default terminal configuration for most applications. All initialization functions
28//!   return this type.
29//!
30//! ## Functions
31//!
32//! The module provides several related functions that handle different initialization scenarios:
33//!
34//! - [`run`] - Initializes a terminal, runs a closure, and automatically restores the terminal
35//!   state. This is the simplest way to run a Ratatui application and handles all setup and cleanup
36//!   automatically.
37//! - [`init`] - Creates a terminal with reasonable defaults including alternate screen and raw
38//!   mode. Panics on failure.
39//! - [`try_init`] - Same as [`init`] but returns a `Result` instead of panicking.
40//! - [`init_with_options`] - Creates a terminal with custom [`TerminalOptions`], enabling raw mode
41//!   but not alternate screen. Panics on failure.
42//! - [`try_init_with_options`] - Same as [`init_with_options`] but returns a `Result` instead of
43//!   panicking.
44//! - [`restore`] - Restores the terminal to its original state. Prints errors to stderr but does
45//!   not panic.
46//! - [`try_restore`] - Same as [`restore`] but returns a `Result` instead of printing errors.
47//!
48//! # Usage Guide
49//!
50//! For the simplest setup with automatic cleanup, use [`run`]:
51//!
52//! ```rust,no_run
53//! fn main() -> std::io::Result<()> {
54//!     ratatui::run(|terminal| {
55//!         loop {
56//!             terminal.draw(|frame| frame.render_widget("Hello, world!", frame.area()))?;
57//!             if crossterm::event::read()?.is_key_press() {
58//!                 break Ok(());
59//!             }
60//!         }
61//!     })
62//! }
63//! ```
64//!
65//! For standard full-screen applications with manual control over initialization and cleanup:
66//!
67//! ```rust,no_run
68//! // Using init() - panics on failure
69//! let mut terminal = ratatui::init();
70//! // ... app logic ...
71//! ratatui::restore();
72//!
73//! // Using try_init() - returns Result for custom error handling
74//! let mut terminal = ratatui::try_init()?;
75//! // ... app logic ...
76//! ratatui::try_restore()?;
77//! # Ok::<(), std::io::Error>(())
78//! ```
79//!
80//! For applications that need custom terminal behavior (inline rendering, custom viewport sizes,
81//! or applications that don't want alternate screen buffer):
82//!
83//! ```rust,no_run
84//! use ratatui::{TerminalOptions, Viewport};
85//!
86//! let options = TerminalOptions {
87//!     viewport: Viewport::Inline(10),
88//! };
89//!
90//! // Using init_with_options() - panics on failure
91//! let mut terminal = ratatui::init_with_options(options);
92//! // ... app logic ...
93//! ratatui::restore();
94//!
95//! // Using try_init_with_options() - returns Result for custom error handling
96//! let options = TerminalOptions {
97//!     viewport: Viewport::Inline(10),
98//! };
99//! let mut terminal = ratatui::try_init_with_options(options)?;
100//! // ... app logic ...
101//! ratatui::try_restore()?;
102//! # Ok::<(), std::io::Error>(())
103//! ```
104//!
105//! For cleanup, use [`restore`] in most cases where you want to attempt restoration but don't need
106//! to handle errors (they are printed to stderr). Use [`try_restore`] when you need to handle
107//! restoration errors, perhaps to retry or provide user feedback.
108//!
109//! Once you have a terminal set up, continue with the main loop to [draw the
110//! UI](`crate#drawing-the-ui`) and [handle events](`crate#handling-events`). See the [main crate
111//! documentation](`crate`) for comprehensive examples of complete applications.
112//!
113//! # Key Differences
114//!
115//! | Function | Alternate Screen | Raw Mode | Error Handling | Use Case |
116//! |----------|------------------|----------|----------------|----------|
117//! | [`run`] | ✓ | ✓ | Auto-cleanup | Simple apps |
118//! | [`init`] | ✓ | ✓ | Panic | Standard full-screen apps |
119//! | [`try_init`] | ✓ | ✓ | Result | Standard apps with error handling |
120//! | [`init_with_options`] | ✗ | ✓ | Panic | Custom viewport apps |
121//! | [`try_init_with_options`] | ✗ | ✓ | Result | Custom viewport with error handling |
122//!
123//! # Panic Hook
124//!
125//! All initialization functions install a panic hook that automatically restores the terminal
126//! state before panicking. This ensures that even if your application panics, the terminal will
127//! be left in a usable state.
128//!
129//! **Important**: Call the initialization functions *after* installing any other panic hooks to
130//! ensure the terminal is restored before other hooks run.
131
132use std::io::{self, Stdout, stdout};
133
134use ratatui_core::terminal::{Terminal, TerminalOptions};
135use ratatui_crossterm::CrosstermBackend;
136use ratatui_crossterm::crossterm::execute;
137use ratatui_crossterm::crossterm::terminal::{
138    EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
139};
140
141/// A type alias for the default terminal type.
142///
143/// This is a [`Terminal`] using the [`CrosstermBackend`] which writes to [`Stdout`]. This is a
144/// reasonable default for most applications. To use a different backend or output stream, instead
145/// use [`Terminal`] and a [backend][`crate::backend`] of your choice directly.
146pub type DefaultTerminal = Terminal<CrosstermBackend<Stdout>>;
147
148/// Run a closure with a terminal initialized with reasonable defaults for most applications.
149///
150/// This function creates a new [`DefaultTerminal`] with [`init`] and then runs the given closure
151/// with a mutable reference to the terminal. After the closure completes, the terminal is restored
152/// to its original state with [`restore`].
153///
154/// This function is a convenience wrapper around [`init`] and [`restore`], and is useful for simple
155/// applications that need a terminal with reasonable defaults for the entire lifetime of the
156/// application.
157///
158/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
159/// functions and guidance on when to use each one.
160///
161/// # Examples
162///
163/// A simple example where the app logic is contained in the closure:
164///
165/// ```rust,no_run
166/// use crossterm::event;
167///
168/// fn main() -> Result<(), Box<dyn std::error::Error>> {
169///     ratatui::run(|terminal| {
170///         loop {
171///             terminal.draw(|frame| frame.render_widget("Hello, world!", frame.area()))?;
172///             if event::read()?.is_key_press() {
173///                 break Ok(());
174///             }
175///         }
176///     })
177/// }
178/// ```
179///
180/// A more complex example where the app logic is contained in a separate function:
181///
182/// ```rust,no_run
183/// use crossterm::event;
184///
185/// type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
186///
187/// fn main() -> Result<()> {
188///     ratatui::run(app)
189/// }
190///
191/// fn app(terminal: &mut ratatui::DefaultTerminal) -> Result<()> {
192///     const GREETING: &str = "Hello, world!";
193///     loop {
194///         terminal.draw(|frame| frame.render_widget(format!("{GREETING}"), frame.area()))?;
195///         if matches!(event::read()?, event::Event::Key(_)) {
196///             break Ok(());
197///         }
198///     }
199/// }
200/// ```
201///
202/// Once the app logic becomes more complex, it may be beneficial to move the app logic into a
203/// separate struct. This allows the app logic to be split into multiple methods with each having
204/// access to the state of the app. This can make the app logic easier to understand and maintain.
205///
206/// ```rust,no_run
207/// use crossterm::event;
208///
209/// type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
210///
211/// fn main() -> Result<()> {
212///     let mut app = App::new();
213///     ratatui::run(|terminal| app.run(terminal))
214/// }
215///
216/// struct App {
217///     should_quit: bool,
218///     name: String,
219/// }
220///
221/// impl App {
222///     fn new() -> Self {
223///         Self {
224///             should_quit: false,
225///             name: "world".to_string(),
226///         }
227///     }
228///
229///     fn run(&mut self, terminal: &mut ratatui::DefaultTerminal) -> Result<()> {
230///         while !self.should_quit {
231///             terminal.draw(|frame| frame.render_widget("Hello, world!", frame.area()))?;
232///             self.handle_events()?;
233///         }
234///         Ok(())
235///     }
236///
237///     fn render(&mut self, frame: &mut ratatui::Frame) -> Result<()> {
238///         let greeting = format!("Hello, {}!", self.name);
239///         frame.render_widget(greeting, frame.area());
240///         Ok(())
241///     }
242///
243///     fn handle_events(&mut self) -> Result<()> {
244///         if event::read()?.is_key_press() {
245///             self.should_quit = true;
246///         }
247///         Ok(())
248///     }
249/// }
250/// ```
251pub fn run<F, R>(f: F) -> R
252where
253    F: FnOnce(&mut DefaultTerminal) -> R,
254{
255    let mut terminal = init();
256    let result = f(&mut terminal);
257    restore();
258    result
259}
260
261/// Initialize a terminal with reasonable defaults for most applications.
262///
263/// This will create a new [`DefaultTerminal`] and initialize it with the following defaults:
264///
265/// - Backend: [`CrosstermBackend`] writing to [`Stdout`]
266/// - Raw mode is enabled
267/// - Alternate screen buffer enabled
268/// - A panic hook is installed that restores the terminal before panicking. Ensure that this method
269///   is called after any other panic hooks that may be installed to ensure that the terminal is
270///   restored before those hooks are called.
271///
272/// For more control over the terminal initialization, use [`Terminal::new`] or
273/// [`Terminal::with_options`].
274///
275/// Ensure that this method is called *after* your app installs any other panic hooks to ensure the
276/// terminal is restored before the other hooks are called.
277///
278/// Generally, use this function instead of [`try_init`] to ensure that the terminal is restored
279/// correctly if any of the initialization steps fail. If you need to handle the error yourself, use
280/// [`try_init`] instead.
281///
282/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
283/// functions and guidance on when to use each one.
284///
285/// # Panics
286///
287/// This function will panic if any of the following steps fail:
288///
289/// - Enabling raw mode
290/// - Entering the alternate screen buffer
291/// - Creating the terminal fails due to being unable to calculate the terminal size
292///
293/// # Examples
294///
295/// ```rust,no_run
296/// let terminal = ratatui::init();
297/// ```
298pub fn init() -> DefaultTerminal {
299    try_init().expect("failed to initialize terminal")
300}
301
302/// Try to initialize a terminal using reasonable defaults for most applications.
303///
304/// This function will attempt to create a [`DefaultTerminal`] and initialize it with the following
305/// defaults:
306///
307/// - Raw mode is enabled
308/// - Alternate screen buffer enabled
309/// - A panic hook is installed that restores the terminal before panicking.
310/// - A [`Terminal`] is created using [`CrosstermBackend`] writing to [`Stdout`]
311///
312/// If any of these steps fail, the error is returned.
313///
314/// Ensure that this method is called *after* your app installs any other panic hooks to ensure the
315/// terminal is restored before the other hooks are called.
316///
317/// Generally, you should use [`init`] instead of this function, as the panic hook installed by this
318/// function will ensure that any failures during initialization will restore the terminal before
319/// panicking. This function is provided for cases where you need to handle the error yourself.
320///
321/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
322/// functions and guidance on when to use each one.
323///
324/// # Examples
325///
326/// ```no_run
327/// let terminal = ratatui::try_init()?;
328/// # Ok::<(), std::io::Error>(())
329/// ```
330pub fn try_init() -> io::Result<DefaultTerminal> {
331    set_panic_hook();
332    enable_raw_mode()?;
333    execute!(stdout(), EnterAlternateScreen)?;
334    let backend = CrosstermBackend::new(stdout());
335    Terminal::new(backend)
336}
337
338/// Initialize a terminal with the given options and reasonable defaults.
339///
340/// This function allows the caller to specify a custom [`Viewport`] via the [`TerminalOptions`]. It
341/// will create a new [`DefaultTerminal`] and initialize it with the given options and the following
342/// defaults:
343///
344/// [`Viewport`]: crate::Viewport
345///
346/// - Raw mode is enabled
347/// - A panic hook is installed that restores the terminal before panicking.
348///
349/// Unlike [`init`], this function does not enter the alternate screen buffer as this may not be
350/// desired in all cases. If you need the alternate screen buffer, you should enable it manually
351/// after calling this function.
352///
353/// For more control over the terminal initialization, use [`Terminal::with_options`].
354///
355/// Ensure that this method is called *after* your app installs any other panic hooks to ensure the
356/// terminal is restored before the other hooks are called.
357///
358/// Generally, use this function instead of [`try_init_with_options`] to ensure that the terminal is
359/// restored correctly if any of the initialization steps fail. If you need to handle the error
360/// yourself, use [`try_init_with_options`] instead.
361///
362/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
363/// functions and guidance on when to use each one.
364///
365/// # Panics
366///
367/// This function will panic if any of the following steps fail:
368///
369/// - Enabling raw mode
370/// - Creating the terminal fails due to being unable to calculate the terminal size
371///
372/// # Examples
373///
374/// ```rust,no_run
375/// use ratatui::{TerminalOptions, Viewport};
376///
377/// let options = TerminalOptions {
378///     viewport: Viewport::Inline(5),
379/// };
380/// let terminal = ratatui::init_with_options(options);
381/// ```
382pub fn init_with_options(options: TerminalOptions) -> DefaultTerminal {
383    try_init_with_options(options).expect("failed to initialize terminal")
384}
385
386/// Try to initialize a terminal with the given options and reasonable defaults.
387///
388/// This function allows the caller to specify a custom [`Viewport`] via the [`TerminalOptions`]. It
389/// will attempt to create a [`DefaultTerminal`] and initialize it with the given options and the
390/// following defaults:
391///
392/// [`Viewport`]: crate::Viewport
393///
394/// - Raw mode is enabled
395/// - A panic hook is installed that restores the terminal before panicking.
396///
397/// Unlike [`try_init`], this function does not enter the alternate screen buffer as this may not be
398/// desired in all cases. If you need the alternate screen buffer, you should enable it manually
399/// after calling this function.
400///
401/// If any of these steps fail, the error is returned.
402///
403/// Ensure that this method is called *after* your app installs any other panic hooks to ensure the
404/// terminal is restored before the other hooks are called.
405///
406/// Generally, you should use [`init_with_options`] instead of this function, as the panic hook
407/// installed by this function will ensure that any failures during initialization will restore the
408/// terminal before panicking. This function is provided for cases where you need to handle the
409/// error yourself.
410///
411/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
412/// functions and guidance on when to use each one.
413///
414/// # Examples
415///
416/// ```no_run
417/// use ratatui::{TerminalOptions, Viewport};
418///
419/// let options = TerminalOptions {
420///     viewport: Viewport::Inline(5),
421/// };
422/// let terminal = ratatui::try_init_with_options(options)?;
423/// # Ok::<(), std::io::Error>(())
424/// ```
425pub fn try_init_with_options(options: TerminalOptions) -> io::Result<DefaultTerminal> {
426    set_panic_hook();
427    enable_raw_mode()?;
428    let backend = CrosstermBackend::new(stdout());
429    Terminal::with_options(backend, options)
430}
431
432/// Restores the terminal to its original state.
433///
434/// This function should be called before the program exits to ensure that the terminal is
435/// restored to its original state.
436///
437/// This function will attempt to restore the terminal to its original state by performing the
438/// following steps:
439///
440/// 1. Raw mode is disabled.
441/// 2. The alternate screen buffer is left.
442///
443/// If either of these steps fail, the error is printed to stderr and ignored.
444///
445/// Use this function over [`try_restore`] when you don't need to handle the error yourself, as
446/// ignoring the error is generally the correct behavior when cleaning up before exiting. If you
447/// need to handle the error yourself, use [`try_restore`] instead.
448///
449/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
450/// functions and guidance on when to use each one.
451///
452/// # Examples
453///
454/// ```rust,no_run
455/// ratatui::restore();
456/// ```
457pub fn restore() {
458    if let Err(err) = try_restore() {
459        // There's not much we can do if restoring the terminal fails, so we just print the error
460        std::eprintln!("Failed to restore terminal: {err}");
461    }
462}
463
464/// Restore the terminal to its original state.
465///
466/// This function will attempt to restore the terminal to its original state by performing the
467/// following steps:
468///
469/// 1. Raw mode is disabled.
470/// 2. The alternate screen buffer is left.
471///
472/// If either of these steps fail, the error is returned.
473///
474/// Use [`restore`] instead of this function when you don't need to handle the error yourself, as
475/// ignoring the error is generally the correct behavior when cleaning up before exiting. If you
476/// need to handle the error yourself, use this function instead.
477///
478/// See the [module-level documentation](mod@crate::init) for a comparison of all initialization
479/// functions and guidance on when to use each one.
480///
481/// # Examples
482///
483/// ```no_run
484/// ratatui::try_restore()?;
485/// # Ok::<(), std::io::Error>(())
486/// ```
487pub fn try_restore() -> io::Result<()> {
488    // disabling raw mode first is important as it has more side effects than leaving the alternate
489    // screen buffer
490    disable_raw_mode()?;
491    execute!(stdout(), LeaveAlternateScreen)?;
492    Ok(())
493}
494
495/// Sets a panic hook that restores the terminal before panicking.
496///
497/// Replaces the panic hook with a one that will restore the terminal state before calling the
498/// original panic hook. This ensures that the terminal is left in a good state when a panic occurs.
499fn set_panic_hook() {
500    let hook = std::panic::take_hook();
501    std::panic::set_hook(alloc::boxed::Box::new(move |info| {
502        restore();
503        hook(info);
504    }));
505}