Skip to main content

retroglyph/backend/
mod.rs

1//! Pluggable rendering backends.
2
3#[cfg(feature = "crossterm")]
4pub mod crossterm;
5pub mod headless;
6#[cfg(feature = "software")]
7pub mod software;
8
9#[cfg(feature = "crossterm")]
10pub use crossterm::Crossterm;
11pub use headless::Headless;
12#[cfg(feature = "software")]
13pub use software::{SoftwareBackend, SoftwareRenderer, WindowedBackend};
14
15use crate::event::Event;
16use crate::grid::{Pos, Size};
17use crate::tile::Tile;
18use core::time::Duration;
19
20/// A rendering backend that presents grid content to a display
21/// and provides input events.
22pub trait Backend {
23    /// Draw changed cells to the output surface.
24    fn draw<'a, I>(&mut self, content: I)
25    where
26        I: Iterator<Item = (Pos, &'a Tile)>;
27
28    /// Draw changed cells across all layers.
29    ///
30    /// The default implementation forwards layer-0 tiles to [`draw`](Self::draw)
31    /// and ignores higher layers. Override this to support multi-layer
32    /// compositing, sub-cell offsets, or transparency.
33    ///
34    /// When [`needs_full_frame`](Self::needs_full_frame) returns `true`, this
35    /// receives **all** cells from every allocated layer, and the backend
36    /// should clear its output surface before drawing.
37    fn draw_layers<'a, I>(&mut self, content: I)
38    where
39        I: Iterator<Item = (u8, Pos, &'a Tile)>,
40    {
41        self.draw(content.filter_map(
42            |(layer, pos, tile)| {
43                if layer == 0 { Some((pos, tile)) } else { None }
44            },
45        ));
46    }
47
48    /// Returns `true` if the backend needs the **entire** frame (all cells on
49    /// all layers) on every call to [`draw_layers`](Self::draw_layers), rather
50    /// than just the changed cells.
51    ///
52    /// Pixel-based backends (e.g. `SoftwareRenderer`) need this because
53    /// sub-cell offsets can spill glyph pixels into adjacent cells — without
54    /// a full redraw, orphaned pixels from the previous frame linger.
55    ///
56    /// The default implementation returns `false`.
57    fn needs_full_frame(&self) -> bool {
58        false
59    }
60
61    /// Flush buffered output to the display.
62    fn flush(&mut self);
63
64    /// Return current display dimensions.
65    #[must_use]
66    fn size(&self) -> Size;
67
68    /// Clear the entire display.
69    fn clear(&mut self);
70
71    /// Notify the backend of a terminal resize.
72    ///
73    /// Called automatically by [`crate::Terminal::resize`] after both grids are resized.
74    /// Backends that maintain internal state tied to terminal dimensions (such as
75    /// [`Headless`]) should override this to update that state. The default
76    /// implementation is a no-op.
77    fn resize(&mut self, size: Size) {
78        let _ = size;
79    }
80
81    /// Poll for an input event, waiting up to `timeout`.
82    fn poll_event(&mut self, timeout: Duration) -> Option<Event>;
83
84    /// Returns `false` if the backend has been disconnected from its
85    /// output (e.g. the window was closed). The game loop should
86    /// terminate when this returns `false`.
87    ///
88    /// The default implementation always returns `true`. Override for
89    /// backends that can detect disconnect.
90    fn is_connected(&self) -> bool {
91        true
92    }
93
94    /// Show or hide the cursor.
95    fn set_cursor_visible(&mut self, visible: bool);
96
97    /// Move the cursor to a position.
98    fn set_cursor_position(&mut self, position: Pos);
99
100    /// Push an event into the backend's event buffer.
101    ///
102    /// Backends that receive events externally (e.g., from a window event
103    /// loop or a test harness) override this to queue events for
104    /// [`poll_event`](Self::poll_event).  The default is a no-op.
105    ///
106    /// - Windowed backends: called by `ApplicationHandler` on each event.
107    /// - Headless: called by tests to inject synthetic events.
108    /// - Crossterm: reads from its own event stream; no-op here.
109    fn push_event(&mut self, _event: Event) {}
110}