pub trait Presenter: Output {
type SurfaceError: RecoverableError;
// Required methods
fn init_surface(
&mut self,
window: Arc<dyn WindowHandle>,
) -> Result<(), Self::SurfaceError>;
fn resize_surface(&mut self, width: u32, height: u32);
fn present(&mut self) -> Result<(), Self::SurfaceError>;
fn cell_size(&self) -> (u32, u32);
// Provided methods
fn scale_factor_changed(&mut self, _scale_factor: f64) { ... }
fn geometry(&self) -> CellGeometry { ... }
}Expand description
A renderer that rasterizes grid content and presents it to a window surface.
A supertrait of Output, adding the surface lifecycle (init_surface, resize_surface,
present, cell_size) that the event loop drives. Every Presenter implementation is an
Output implementation for free: WindowBackend delegates its own
Output impl straight through to P: Presenter, with no duplicated method bodies.
§Sub-cell offsets and spill
A Tile’s dx/dy shift its glyph within, and past, its cell.
This is a cross-backend rendering contract: the CPU rasterizer (retroglyph-software) and the
GPU ones (retroglyph-gl, retroglyph-wgpu) must produce the same pixels, so it is specified
here once instead of in mirrored per-backend comments that reference each other (and drift when
only one is touched). A Presenter that honors sub-cell offsets must obey all four points:
dx/dyare in unscaled font pixels (a presenter multiplies by its own integer scale); negativedxshifts the glyph left, negativedyup.- The cell’s background fill is always the full, unshifted cell rectangle. An offset moves only the glyph, never the background.
- An offset glyph may spill past its cell edge into neighboring cells, and that spill is uniform in all four directions: a glyph pushed right/down onto a later neighbor spills the same way as one pushed left/up onto an earlier neighbor.
- The mechanism that guarantees that uniformity is a two-pass draw: lay down every cell’s background first, then draw every cell’s (offset) glyph over the result. Interleaving the two per cell would let a later cell’s background overwrite an earlier neighbor’s spilled glyph, breaking spill in the right/down directions only.
The offset application is not shared code: the GPU backends shift a quad’s vertex position in
their vertex shader, retroglyph-software shifts origin_x/origin_y in a CPU blit:
irreducibly different mechanics that must nonetheless agree on the four points above.
§Examples
use retroglyph_core::backend::{DrawCell, Output};
use retroglyph_core::grid::Size;
use retroglyph_window::{Presenter, WindowHandle};
use std::sync::Arc;
struct NullPresenter;
impl Output for NullPresenter {
type Error = core::convert::Infallible;
fn draw_layers<'a, I>(&mut self, _content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = DrawCell<'a>>,
{
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn size(&self) -> Size {
Size::new(4, 2)
}
fn clear(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Presenter for NullPresenter {
type SurfaceError = core::convert::Infallible;
fn init_surface(&mut self, _window: Arc<dyn WindowHandle>) -> Result<(), Self::SurfaceError> {
Ok(())
}
fn resize_surface(&mut self, _width: u32, _height: u32) {}
fn present(&mut self) -> Result<(), Self::SurfaceError> {
Ok(())
}
fn cell_size(&self) -> (u32, u32) {
(8, 16)
}
}Required Associated Types§
Sourcetype SurfaceError: RecoverableError
type SurfaceError: RecoverableError
Surface lifecycle error (context creation, buffer acquisition, present).
Required Methods§
Sourcefn init_surface(
&mut self,
window: Arc<dyn WindowHandle>,
) -> Result<(), Self::SurfaceError>
fn init_surface( &mut self, window: Arc<dyn WindowHandle>, ) -> Result<(), Self::SurfaceError>
Initialize the window surface.
Called once from the loop’s resumed handler. The presenter creates its platform surface
(softbuffer surface, wgpu device+surface, GL context) from the raw window/display handles.
§Errors
Returns Self::SurfaceError if surface or context creation from window fails (an
unsupported display/window system connection, a missing graphics API, or, on wasm32,
a canvas element that can’t be located). Unlike a failed present,
the winit driver treats this as fatal rather than retryable: it logs the error and
exits the event loop immediately, since there is no surface to draw into and no later
hook that calls init_surface again.
Sourcefn resize_surface(&mut self, width: u32, height: u32)
fn resize_surface(&mut self, width: u32, height: u32)
Resize the window surface to a new physical pixel size.
Called on every window resize event with width/height already resolved by the
caller: for the winit driver (see winit::run::WindowApp::resize_to), that means
cols * cell_w x rows * cell_h, where cols/rows are the window’s physical size
divided down to whole cells. Any sub-cell remainder is truncated, not centered or
cleared: when the window’s physical size isn’t an exact multiple of the cell size,
width/height here are the largest whole-cell-multiple that fits, which can be
smaller than the window’s actual physical size. The OS window itself is never resized
to compensate, so a non-exact-multiple resize leaves a thin strip at the window’s
trailing edge outside the surface: retroglyph does not paint or clear that strip;
whatever the OS/windowing backend leaves there remains visible until a subsequent
resize covers it.
Sourcefn present(&mut self) -> Result<(), Self::SurfaceError>
fn present(&mut self) -> Result<(), Self::SurfaceError>
Present the rasterized frame to the window surface.
Called after each app tick. A lost frame is not fatal; the caller logs the error and continues.
§Errors
Returns Self::SurfaceError if the surface buffer can’t be acquired or presented (e.g.
context lost on wasm, page flip pending on DRI/KMS).
Sourcefn cell_size(&self) -> (u32, u32)
fn cell_size(&self) -> (u32, u32)
Cell size in physical pixels (width, height).
Physical pixels, not logical/DPI-scaled pixels, and never auto-scaled by this crate for
display DPI: see the crate-level “DPI, scale, and the resize contract” docs. A presenter
whose cells should grow on a HiDPI display must change what this returns itself (from
resize or scale_factor_changed); absent
that, it stays constant for the presenter’s lifetime.
(u32, u32) rather than Size because grid coordinates
are u16 but pixel arithmetic uses u32 (winit PhysicalSize).
Provided Methods§
Sourcefn scale_factor_changed(&mut self, _scale_factor: f64)
fn scale_factor_changed(&mut self, _scale_factor: f64)
Notify the presenter that the window’s scale factor (DPI) changed.
Called when the window moves to a display with a different pixel density, or the
system DPI setting changes. The event loop follows this with
resize_surface for the window’s new physical size, so
this hook only needs to handle DPI-dependent state that isn’t a plain buffer
resize (e.g. regenerating a font atlas rasterized for a particular scale).
Defaults to a no-op: presenters whose rasterization doesn’t depend on DPI (like
SoftwareRenderer’s integer scale config, set once at construction) need no
action here.
Sourcefn geometry(&self) -> CellGeometry
fn geometry(&self) -> CellGeometry
This presenter’s cell geometry, as a CellGeometry rather than the raw
(width, height) pair cell_size returns.
Lets callers (e.g. winit::run’s cursor/mouse handlers) use
CellGeometry::pixel_to_cell directly instead of pairing a raw cell_size() with the
translate_pixel_to_cell free
function. The default implementation derives a geometry from cell_size
at scale 1, clamping each dimension to u8::MAX: exact for presenters whose cell size
fits in a u8 (true of every glyph/tile size in practice), lossy only past that, which
only affects pixel_to_cell precision for callers that
don’t override this method (test doubles, not retroglyph-software/retroglyph-gl, both
of which override it to return their real internal geometry).
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.