retroglyph_core/lib.rs
1//! retroglyph-core: the `no_std`-compatible foundation of retroglyph.
2//!
3//! Grid, tile, style, color, text, terminal, and event types, plus the
4//! [`Output`](crate::backend::Output)/[`Input`](crate::backend::Input)/[`Cursor`](crate::backend::Cursor) backend
5//! facets (bundled together as [`Backend`](crate::backend::Backend)) and the dependency-free
6//! [`Headless`](crate::backend::Headless) test backend, and the `App`/`Flow`/`Frame` game loop contract.
7//! Platform backends (`retroglyph-crossterm`, `retroglyph-software`) and drawing helpers
8//! (`retroglyph-ui`) are separate crates that depend on this one.
9//!
10//! # Features
11//!
12//! <!-- gen-features:start -->
13//! Default features: `egc`, `std`.
14//!
15//! ### `dev`
16//!
17//! ⚪ Optional.
18//!
19//! Forces `BuildMode::Dev` on in a build that would otherwise resolve to `Release`.
20//!
21//! Can be used so an optimized build still reports development diagnostics (see the [`dev`]
22//! module).
23//!
24//! ### `egc`
25//!
26//! 🟢 Enabled by default.
27//!
28//! Enables grapheme-cluster-aware text handling (via `unicode-segmentation`) for EGC-correct cell
29//! diffing and layout.
30//!
31//! ### `libm`
32//!
33//! ⚪ Optional.
34//!
35//! Uses `libm`'s software float implementation (`roundf`/`fmaf`/`sinf`/`cosf`/`powf`) for
36//! the separable [`BlendMode`](crate::grid::BlendMode) channel math, via this crate's own
37//! `math` shim -- the `no_std` side of that split. See `std` below for the alternative that prefers
38//! the platform's own float intrinsics when available; a build needs exactly one of the two.
39//!
40//! ### `serde`
41//!
42//! ⚪ Optional.
43//!
44//! Adds `Serialize`/`Deserialize` impls for [`Color`](crate::color::Color), [`Style`](crate::color::Style), `Size`,
45//! `Offset`, and (via `ixy`) `Pos`/`Rect`, so a config file can round-trip a saved camera position,
46//! window geometry, sub-cell pixel offset, or theme color.
47//!
48//! [`Color`](crate::color::Color) serializes through its `Display`/`FromStr` round trip (e.g. `"bright-red"`,
49//! `"#ff8000"`) rather than a derived structural form, so hand-edited TOML/JSON stays legible.
50//!
51//! ### `std`
52//!
53//! 🟢 Enabled by default.
54//!
55//! Enables `gem/std` and `alpha-blend/std`, and uses `std`'s float intrinsics (via this crate's
56//! `math` shim) instead of `libm`'s software implementation for the separable
57//! [`BlendMode`](crate::grid::BlendMode) channel math.
58//!
59//! Disabling this feature (`--no-default-features`) builds this crate `no_std`, and then needs
60//! `libm` above as the float backend instead: see the crate-level `compile_error!` in `src/lib.rs`.
61//!
62//! ### `testing`
63//!
64//! ⚪ Optional.
65//!
66//! Enables `testing`'s `TestHarness`, which drives an [`App`](crate::app::App) against
67//! [`Headless`](crate::backend::Headless) for tests, with
68//! synthetic input queuing and frame-settling helpers.
69//!
70//! Test-only surface, `no_std` + `alloc` compatible, off by default so it never ships in a release
71//! build by accident.
72//! <!-- gen-features:end -->
73//!
74//! # Stability
75//!
76//! [`Pos`](crate::grid::Pos), [`Size`](crate::grid::Size), and [`Rect`](crate::grid::Rect) are bare
77//! aliases onto `ixy`'s own types, and [`Tint::apply_rgb888`](crate::color::Tint::apply_rgb888) takes
78//! and returns `gem`'s `Rgb888` directly (see `crates/core/Cargo.toml`'s `ixy`/`gem` entries for the
79//! pinned versions). Rather than newtype-wrap those crates, retroglyph's stability guarantee
80//! covers the pinned major version of `ixy` and `gem` along with retroglyph's own API: a
81//! `retroglyph 1.x` release promises the `ixy` and `gem` major versions it was built against will
82//! not change underneath a `1.x` consumer either, not just retroglyph's own surface. See
83//! `RELEASING.md` in the workspace root for the release-process side of that commitment.
84//!
85//! # Architecture
86//!
87//! [`Terminal<B>`](crate::terminal::Terminal) owns a double-buffered [`Grid`](crate::grid::Grid) and the
88//! [`Backend`](crate::backend::Backend) lifecycle (resize, present, events), diffing each frame drawn through
89//! [`Surface`](crate::surface::Surface) against the previous one so only changed cells reach the
90//! [`Backend`](crate::backend::Backend). `B` is the only thing that changes between a headless test and a real
91//! window or terminal: [`Headless`](crate::backend::Headless) here, `Crossterm` (retroglyph-crossterm) for a
92//! real TTY, `SoftwareRenderer`/`GlRenderer`/`WgpuRenderer` for a window or browser tab. See
93//! <https://main.retroglyph.dev/book/explanation/architecture.html> for the full data-flow diagram
94//! and how those backends compare.
95//!
96//! See `examples/headless.rs` (`cargo run -p retroglyph-core --example
97//! headless`) for the smallest possible use of [`Headless`](crate::backend::Headless), depending on
98//! nothing but this crate.
99#![cfg_attr(not(feature = "std"), no_std)]
100#![doc(
101 html_logo_url = "https://raw.githubusercontent.com/crates-lurey-io/retroglyph/main/docs/public/assets/logo.svg"
102)]
103#![doc(
104 html_favicon_url = "https://raw.githubusercontent.com/crates-lurey-io/retroglyph/main/docs/public/assets/logo.svg"
105)]
106#![cfg_attr(docsrs, feature(doc_cfg))]
107// A `pub mod` line's own outer doc comment and its target module's inner `//!` doc concatenate
108// into one rendered page, but intra-doc links in that combined block resolve against the scope
109// where the *outer* comment lives (this file, the crate root) rather than the module's own scope.
110// Every module doc below that also carries an outer doc comment on its `pub mod` line therefore
111// needs fully qualified links even for types the module defines itself, which then reads as
112// "redundant" from the module file's own point of view. Rather than track that split per link,
113// every intra-doc link in this crate is fully qualified and this lint is off crate-wide.
114#![allow(rustdoc::redundant_explicit_links)]
115extern crate alloc;
116
117// A float backend is not optional (retroglyph#903): the separable `BlendMode` channel math
118// dispatches through `crate::math`, which has nothing to dispatch *to* without one, and
119// `Color`'s color-space conversions go through `gem/space`, which needs `gem/std` or `gem/libm`
120// for the same reason. Failing here names the two features that fix it, ahead of the same build
121// failing as an unresolved `libm::` path inside `math.rs` or inside `gem::space`'s own
122// `compile_error!`.
123#[cfg(not(any(feature = "std", feature = "libm")))]
124compile_error!("retroglyph-core needs a float backend: enable `std` or `libm`.");
125
126// Compile the code blocks in this crate's own README as doctests so its quick start is
127// type-checked on every test run and cannot silently rot. The `cfg(doctest)` gate keeps this out
128// of the rendered crate documentation: see `retroglyph-crossterm`'s matching include for the
129// same pattern applied to the workspace root README.
130#[cfg(doctest)]
131#[doc = include_str!("../README.md")]
132struct ReadmeDoctests;
133
134/// The `App`-driven game loop.
135pub mod app;
136// See the `too_long_first_doc_paragraph` comment above `animate`: same noisy-lint mis-attribution,
137// here because this module's own first doc paragraph grew past the threshold once its intra-doc
138// links became fully qualified (retroglyph#1035).
139#[allow(clippy::too_long_first_doc_paragraph)]
140/// Pluggable rendering backends.
141pub mod backend;
142pub mod color;
143// See the `too_long_first_doc_paragraph` comment above `animate`: same noisy-lint mis-attribution.
144#[allow(clippy::too_long_first_doc_paragraph)]
145/// Which diagnostics a build compiles in.
146pub mod dev;
147pub mod event;
148// See the `too_long_first_doc_paragraph` comment above `animate`: same noisy-lint mis-attribution.
149#[allow(clippy::too_long_first_doc_paragraph)]
150/// `FrameClock`/`FrameStats` accumulators for the `App`/`Frame` game loop.
151pub mod frames;
152pub mod grid;
153pub mod layout;
154// `pub` so `retroglyph-ui` can share this crate's one std-or-libm dispatch point instead of
155// vendoring its own copy, `#[doc(hidden)]` so that sharing costs no public API surface:
156// `cargo-semver-checks` ignores hidden items (see the module's own doc comment for the traps that
157// come with that). Never add a `pub use` that re-exports its contents through a non-hidden path,
158// and never `#[deprecated]` it, both of which would make it public API again despite the hiding.
159#[doc(hidden)]
160pub mod math;
161// See the `too_long_first_doc_paragraph` comment above `animate`: same noisy-lint mis-attribution.
162#[allow(clippy::too_long_first_doc_paragraph)]
163/// The one grid-drawing primitive: an area-clipped, single-layer view over a [`Grid`](crate::grid::Grid).
164pub mod surface;
165#[allow(clippy::too_long_first_doc_paragraph)]
166/// Border, gridline, and partial-block `char` data shared by widgets and backends.
167pub mod symbols;
168pub mod terminal;
169// See the `too_long_first_doc_paragraph` comment above `animate`: same noisy-lint mis-attribution.
170#[allow(clippy::too_long_first_doc_paragraph)]
171/// Headless test harness driving an `App` with synthetic input.
172#[cfg(feature = "testing")]
173pub mod testing;
174pub mod text;
175/// The atomic drawable unit (glyph, style, sub-cell offsets).
176pub mod tile;
177
178// No root re-exports below this line by design (retroglyph#1035): every public item lives at its
179// module path, matching `ratatui-core`. `dev_only!` (`dev.rs`) and `spans!` (`text.rs`) still
180// resolve at the crate root regardless, since `#[macro_export]` always places a macro there; that's
181// a macro-export constraint, not a re-export choice.