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}