retroglyph_window/lib.rs
1//! A shared layer for window-based backends (software, GL, wgpu).
2//!
3//! # Architecture
4//!
5//! [`retroglyph_core::backend::Input`] and [`retroglyph_core::backend::Output`] are two
6//! independent facets of [`Backend`](retroglyph_core::backend::Backend), which fits a terminal process
7//! (one type implements both) but not a window: there, an event loop owns input and a renderer
8//! owns output separately. This crate keeps that split ([`Presenter`] is an `Output` supertrait,
9//! [`WindowBackend`] owns its own `Input` event queue) and reassembles both into one `Backend`:
10//!
11//! ```text
12//! ┌─────────────────────────────┐
13//! │ event loop (winit or │
14//! │ a custom driver) │
15//! └──────────────┬───────────────┘
16//! translated events
17//! │
18//! v
19//! ┌────────────────────────────────────────────────────┐
20//! │ WindowBackend<P: Presenter> │
21//! │ (implements Backend: owns the input event queue, │
22//! │ delegates output to P) │
23//! └───────────────────────┬──────────────────────────────┘
24//! │ draw / flush / resize / present
25//! v
26//! ┌───────────────────────────────┐
27//! │ P: Presenter │
28//! │ (retroglyph-software today; │
29//! │ wgpu/GL renderers planned) │
30//! └───────────────────────────────┘
31//! ```
32//!
33//! - [`Presenter`] is `Output` plus the surface lifecycle
34//! (`init_surface`/`resize_surface`/`present`/`cell_size`). Renderer crates implement only this
35//! trait, which gives them `Output` for free.
36//! - <code>[WindowBackend]<P: Presenter></code> implements `Output` (by delegating to `P`),
37//! `Input` (via its own event queue), and the no-op default `Cursor` (windowed backends have no
38//! text cursor), which together give it `Backend` generically.
39//! - The `winit` module (feature-gated, see below) drives the event loop that fills that queue
40//! and calls `Presenter::present` each frame.
41//!
42//! # Features
43//!
44//! <!-- gen-features:start -->
45//! Default features: `winit`.
46//!
47//! ### `default-font`
48//!
49//! ⚪ Optional.
50//!
51//! Embeds the Unscii 16 default font (`font::unscii16`).
52//!
53//! Off by default so a consumer that supplies its own bitmap font pays nothing for the ~4 KB atlas;
54//! the graphical backends' own `default-font` features forward to this one.
55//!
56//! ### `dev`
57//!
58//! ⚪ Optional.
59//!
60//! Forwards `retroglyph-core`'s `dev` feature, which forces development diagnostics on in a build
61//! that would otherwise compile them out (see [`retroglyph_core::dev`]).
62//!
63//! Forwarded so a consumer of this crate can turn them on without adding a direct dependency on
64//! core just to reach the flag.
65//!
66//! ### `legacy-computing`
67//!
68//! ⚪ Optional.
69//!
70//! Embeds a generated block-elements/braille fallback font (`font::legacy_computing`): the 10
71//! quadrant, 60 sextant, and 256 braille glyphs CP437 (and so `unscii16`) has no mapping for.
72//!
73//! A separate opt-in from `default-font` rather than folded into it: this repertoire is a much more
74//! niche/specialized addition (subcell image rendering, braille density tricks) than the base text
75//! font, so a consumer that only wants CP437 text shouldn't pay for it. Computed at compile time by
76//! a `const fn`, so this adds no font asset and no new dependency.
77//!
78//! ### `tilesets`
79//!
80//! ⚪ Optional.
81//!
82//! Shared PNG sprite/tileset support (`tileset` + `sprite_cache` modules, issue #366).
83//!
84//! Both graphical backends' own `tilesets` features forward to this one.
85//!
86//! ### `winit`
87//!
88//! 🟢 Enabled by default.
89//!
90//! The winit event loop and event translation (`run`, `translate`, `run_windowed`/`run_app`).
91//!
92//! Renderer crates that only implement [`Presenter`] can disable this and depend solely on
93//! `raw-window-handle`; loops other than winit (SDL2, tao, custom) bring their own driver against
94//! `Presenter` + `WindowBackend`.
95//! <!-- gen-features:end -->
96//!
97//! # Feature flags
98//!
99//! [`Presenter`], [`WindowBackend`], and [`WindowHandle`] depend only on
100//! [`raw-window-handle`](raw_window_handle) and are always available. The `winit` feature
101//! (default on) additionally provides the `winit` module: the event loop, event translation, and
102//! the `run_windowed`/`run_app` drivers. Disable it to implement or drive `Presenter` with a
103//! different windowing library (SDL2, tao, a custom loop) without pulling in winit.
104//!
105//! # DPI, scale, and the resize contract
106//!
107//! [`Presenter::cell_size`] returns the cell size in **physical pixels** (the same pixel
108//! space as `winit::dpi::PhysicalSize`), not logical/DPI-scaled ("CSS" or "point") pixels.
109//! This crate performs no automatic DPI scaling of it: nothing here changes `cell_size()` in
110//! response to a display's scale factor. `SoftwareRenderer`'s cell size, for example, is
111//! fixed at construction (glyph size × its integer `scale` config) and never changes on a
112//! [`Presenter::scale_factor_changed`] notification. A presenter that wants larger cells on a
113//! `HiDPI` display has to opt into that itself from `scale_factor_changed` (e.g. regenerating a
114//! font atlas at a new pixel density); until one does, the grid renders at a fixed physical
115//! pixel size on every display, `HiDPI` or not.
116//!
117//! Window resize is clamped to whole cells: a physical size that isn't an exact multiple of
118//! `cell_size()` has its sub-cell remainder truncated, not centered or cleared, and the OS
119//! window is never resized to compensate: see [`Presenter::resize_surface`]'s doc comment
120//! for the full contract, including the unpainted trailing strip this can leave on screen.
121//!
122//! # Threading model
123//!
124//! The windowed drivers (`winit::run_windowed`, `winit::run_app`, and their `_with_proxy`
125//! variants) are single-threaded: the event loop, every [`Presenter`] call, and the app
126//! closure/[`App`](retroglyph_core::app::App) callback all run on the one thread that calls
127//! `run_windowed`/`run_app`: the main thread, on platforms (e.g. macOS) that require it for
128//! windowing. Neither [`Presenter`] nor [`WindowBackend`] carries a `Send`/`Sync` bound
129//! anywhere in this crate, and a presenter is free to hold thread-affine state accordingly
130//! (an `Rc`, a non-`Send` GPU context handle). The only supported way to reach the loop from
131//! another thread is `winit::EventProxy<T>`, which is `Send + Sync + Clone` for any
132//! `T: Send + 'static`: it does not give another thread direct access to the `Presenter` or
133//! `Terminal`. With the default `T = u64` (`winit::run_windowed_with_proxy`/
134//! `run_app_with_proxy`), the payload surfaces as an opaque
135//! [`Event::Custom`](retroglyph_core::event::Event::Custom); a custom `T`
136//! (`winit::run_windowed_with_typed_proxy`/`run_app_with_typed_proxy`) bypasses `Event` entirely
137//! and goes straight to a caller-supplied handler, since `Event::Custom` itself stays fixed to
138//! `u64`.
139
140#![cfg_attr(docsrs, feature(doc_cfg))]
141
142pub mod atlas;
143// clippy::too_long_first_doc_paragraph is a known-noisy nursery lint (rust-lang/rust-clippy#13441):
144// it mis-attributes its span across this outer doc comment plus `backend`'s own inner module doc,
145// which grew past the threshold once its intra-doc links became fully qualified (retroglyph#1035).
146#[allow(clippy::too_long_first_doc_paragraph)]
147/// The generic [`Backend`](retroglyph_core::backend::Backend) for windowed presenters.
148pub mod backend;
149/// System clipboard read/write ([`Clipboard`], [`SystemClipboard`] on native targets).
150pub mod clipboard;
151pub mod font;
152/// Shared cell/surface pixel geometry ([`CellGeometry`](geometry::CellGeometry)).
153pub mod geometry;
154/// Canonical default colors ([`DEFAULT_FG`](palette::DEFAULT_FG),
155/// [`DEFAULT_BG`](palette::DEFAULT_BG)) shared by the graphical backends.
156pub mod palette;
157/// The [`Presenter`] trait and [`WindowHandle`](presenter::WindowHandle).
158pub mod presenter;
159#[cfg(feature = "tilesets")]
160pub mod sprite_cache;
161#[cfg(feature = "tilesets")]
162pub mod tileset;
163/// Locates winit's `<canvas>` element via the DOM ([`web::winit_canvas`]).
164#[cfg(target_arch = "wasm32")]
165pub mod web;
166/// The winit event loop, event translation, and app drivers.
167#[cfg(feature = "winit")]
168pub mod winit;
169
170// Compile the code blocks in this crate's own README as doctests so its quick start is
171// type-checked on every test run and cannot silently rot. The `cfg(doctest)` gate keeps this out
172// of the rendered crate documentation: see `retroglyph-crossterm`'s matching include for the
173// same pattern applied to the workspace root README.
174#[cfg(doctest)]
175#[doc = include_str!("../README.md")]
176struct ReadmeDoctests;
177
178pub use backend::WindowBackend;
179#[cfg(not(target_arch = "wasm32"))]
180pub use clipboard::SystemClipboard;
181pub use clipboard::{Clipboard, ClipboardError};
182pub use geometry::CellGeometry;
183pub use presenter::{
184 GenericSurfaceError, Presenter, RecoverableError, WindowHandle, cell_art_glyph,
185};
186
187// Re-exported so presenters can name the handle traits without adding their
188// own raw-window-handle dependency (and so versions can't drift apart).
189pub use raw_window_handle;