Skip to main content

retroglyph_window/
presenter.rs

1//! The [`Presenter`] trait: what a renderer crate implements to rasterize a
2//! grid and present it to a window surface.
3//!
4//! `Presenter` is the output half of [`Backend`](retroglyph_core::Backend)
5//! plus window-surface operations, with no input methods: the event loop
6//! owns input, and [`WindowBackend`](crate::WindowBackend) forwards
7//! translated events into its own queue instead.
8//!
9//! | Presenter | `present()` | `init_surface()` |
10//! |---|---|---|
11//! | `SoftwareRenderer` (retroglyph-software) | Copies pixel buffer to softbuffer surface | Creates `softbuffer::Context` + `Surface` |
12//! | `WgpuRenderer` (future) | Submits render pass + presents swap chain | Creates `wgpu::Surface` + `Device` |
13//! | `GlRenderer` (future) | Draws full-screen quad + swaps buffers | Creates GL context from the window |
14
15use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
16use retroglyph_core::backend::BackendError;
17use retroglyph_core::grid::{Pos, Size};
18use retroglyph_core::tile::Tile;
19use std::sync::Arc;
20
21/// A window/display handle pair, as one trait.
22///
23/// Presenters receive [`raw-window-handle`](raw_window_handle) types, not a
24/// concrete `winit::window::Window`: softbuffer, wgpu, and glutin all accept
25/// these handles directly, so any windowing library that produces them can
26/// drive the same presenter, and only this crate depends on winit itself.
27///
28/// `raw-window-handle` has no combined trait, and surface libraries need to
29/// *own* the handle (softbuffer stores it for the surface's lifetime), so
30/// presenters receive `Arc<dyn WindowHandle>` -- rwh implements the handle
31/// traits for `Arc<H: ?Sized>`, so the trait object passes straight into
32/// `softbuffer::Surface::new` / `wgpu::Instance::create_surface`.
33pub trait WindowHandle: HasWindowHandle + HasDisplayHandle {}
34
35impl<T: HasWindowHandle + HasDisplayHandle + ?Sized> WindowHandle for T {}
36
37/// A renderer that rasterizes grid content and presents it to a window
38/// surface.
39///
40/// Mirrors the output half of [`Backend`](retroglyph_core::Backend) (`draw`,
41/// `draw_layers`, `flush`, `size`, `clear`, `resize`) so
42/// [`WindowBackend`](crate::WindowBackend) can delegate those methods
43/// wholesale, and adds the surface lifecycle (`init_surface`,
44/// `resize_surface`, `present`, `cell_size`) that the event loop drives.
45///
46/// The `needs_full_frame` and `composites_layers` defaults are `true`: every
47/// windowed presenter is a pixel-family backend that composites layers
48/// itself, receiving the raw per-layer stream instead of a pre-flattened
49/// single layer. Only character-cell terminal backends return `false`, and
50/// those implement [`Backend`](retroglyph_core::Backend) directly instead of
51/// this trait.
52pub trait Presenter {
53    /// Rasterization error (mirrors `Backend::Error`).
54    ///
55    /// In-memory rasterizers are infallible and use
56    /// [`core::convert::Infallible`].
57    type Error: BackendError;
58
59    /// Surface lifecycle error (context creation, buffer acquisition,
60    /// present).
61    type SurfaceError: core::fmt::Debug + core::fmt::Display;
62
63    /// Rasterize changed cells (single layer).
64    ///
65    /// # Errors
66    ///
67    /// Returns [`Self::Error`] if rasterization fails.
68    fn draw<'a, I>(&mut self, content: I) -> Result<(), Self::Error>
69    where
70        I: Iterator<Item = (Pos, &'a Tile)>;
71
72    /// Rasterize the full layered frame.
73    ///
74    /// Because [`needs_full_frame`](Self::needs_full_frame) defaults to
75    /// `true`, this receives every cell of every allocated layer and should
76    /// clear its target before drawing.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`Self::Error`] if rasterization fails.
81    fn draw_layers<'a, I>(&mut self, content: I) -> Result<(), Self::Error>
82    where
83        I: Iterator<Item = (u8, Pos, &'a Tile)>;
84
85    /// Flush buffered rasterization work.
86    ///
87    /// Distinct from [`present`](Self::present): `flush` completes drawing
88    /// into the presenter's own target; `present` pushes that target to the
89    /// OS window.
90    ///
91    /// # Errors
92    ///
93    /// Returns [`Self::Error`] if the flush fails.
94    fn flush(&mut self) -> Result<(), Self::Error>;
95
96    /// Current grid dimensions in cells.
97    #[must_use]
98    fn size(&self) -> Size;
99
100    /// Clear the rasterization target.
101    ///
102    /// # Errors
103    ///
104    /// Returns [`Self::Error`] if the clear fails.
105    fn clear(&mut self) -> Result<(), Self::Error>;
106
107    /// Resize the grid (in cells), reallocating the rasterization target.
108    fn resize(&mut self, size: Size);
109
110    /// Whether the full frame is required on every `draw_layers` call.
111    ///
112    /// Defaults to `true` for the windowed family (sub-cell offsets spill
113    /// pixels across cells; partial redraws would leave orphans).
114    #[must_use]
115    fn needs_full_frame(&self) -> bool {
116        true
117    }
118
119    /// Whether this presenter composites layers itself, receiving the raw
120    /// `(layer, Pos, Tile)` stream instead of a pre-flattened single layer.
121    ///
122    /// Defaults to `true` for the windowed family.
123    #[must_use]
124    fn composites_layers(&self) -> bool {
125        true
126    }
127
128    /// Initialize the window surface.
129    ///
130    /// Called once from the loop's `resumed` handler. The presenter creates
131    /// its platform surface (softbuffer surface, wgpu device+surface, GL
132    /// context) from the raw window/display handles.
133    ///
134    /// # Errors
135    ///
136    /// Returns [`Self::SurfaceError`] if surface or context creation fails.
137    fn init_surface(&mut self, window: Arc<dyn WindowHandle>) -> Result<(), Self::SurfaceError>;
138
139    /// Resize the window surface to a new physical pixel size.
140    ///
141    /// Called on every window resize event.
142    fn resize_surface(&mut self, width: u32, height: u32);
143
144    /// Present the rasterized frame to the window surface.
145    ///
146    /// Called after each app tick. A lost frame is not fatal; the caller
147    /// logs the error and continues.
148    ///
149    /// # Errors
150    ///
151    /// Returns [`Self::SurfaceError`] if the surface buffer can't be acquired
152    /// or presented (e.g. context lost on wasm, page flip pending on
153    /// DRI/KMS).
154    fn present(&mut self) -> Result<(), Self::SurfaceError>;
155
156    /// Cell size in physical pixels `(width, height)`.
157    ///
158    /// `(u32, u32)` rather than [`Size`] because grid coordinates are `u16`
159    /// but pixel arithmetic uses `u32` (winit `PhysicalSize`).
160    #[must_use]
161    fn cell_size(&self) -> (u32, u32);
162}