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), 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//! # Feature flags
43//!
44//! [`Presenter`], [`WindowBackend`], and [`WindowHandle`] depend only on
45//! [`raw-window-handle`](raw_window_handle) and are always available. The `winit` feature
46//! (default on) additionally provides the `winit` module: the event loop, event translation, and
47//! the `run_windowed`/`run_app` drivers. Disable it to implement or drive `Presenter` with a
48//! different windowing library (SDL2, tao, a custom loop) without pulling in winit.
49//!
50//! # DPI, scale, and the resize contract
51//!
52//! [`Presenter::cell_size`] returns the cell size in **physical pixels** -- the same pixel
53//! space as `winit::dpi::PhysicalSize` -- not logical/DPI-scaled ("CSS" or "point") pixels.
54//! This crate performs no automatic DPI scaling of it: nothing here changes `cell_size()` in
55//! response to a display's scale factor. `SoftwareRenderer`'s cell size, for example, is
56//! fixed at construction (glyph size × its integer `scale` config) and never changes on a
57//! [`Presenter::scale_factor_changed`] notification. A presenter that wants larger cells on a
58//! `HiDPI` display has to opt into that itself from `scale_factor_changed` (e.g. regenerating a
59//! font atlas at a new pixel density); until one does, the grid renders at a fixed physical
60//! pixel size on every display, `HiDPI` or not.
61//!
62//! Window resize is clamped to whole cells: a physical size that isn't an exact multiple of
63//! `cell_size()` has its sub-cell remainder truncated, not centered or cleared, and the OS
64//! window is never resized to compensate -- see [`Presenter::resize_surface`]'s doc comment
65//! for the full contract, including the unpainted trailing strip this can leave on screen.
66//!
67//! # Threading model
68//!
69//! The windowed drivers (`winit::run_windowed`, `winit::run_app`, and their `_with_proxy`
70//! variants) are single-threaded: the event loop, every [`Presenter`] call, and the app
71//! closure/[`App`](retroglyph_core::App) callback all run on the one thread that calls
72//! `run_windowed`/`run_app` -- the main thread, on platforms (e.g. macOS) that require it for
73//! windowing. Neither [`Presenter`] nor [`WindowBackend`] carries a `Send`/`Sync` bound
74//! anywhere in this crate, and a presenter is free to hold thread-affine state accordingly
75//! (an `Rc`, a non-`Send` GPU context handle). The only supported way to reach the loop from
76//! another thread is `winit::EventProxy<T>`, which is `Send + Sync + Clone` for any
77//! `T: Send + 'static` -- it does not give another thread direct access to the `Presenter` or
78//! `Terminal`. With the default `T = u64` (`winit::run_windowed_with_proxy`/
79//! `run_app_with_proxy`), the payload surfaces as an opaque
80//! [`Event::Custom`](retroglyph_core::event::Event::Custom); a custom `T`
81//! (`winit::run_windowed_with_typed_proxy`/`run_app_with_typed_proxy`) bypasses `Event` entirely
82//! and goes straight to a caller-supplied handler, since `Event::Custom` itself stays fixed to
83//! `u64`.
84
85/// The generic [`Backend`](retroglyph_core::Backend) for windowed presenters.
86pub mod backend;
87/// System clipboard read/write ([`Clipboard`], [`SystemClipboard`] on native targets).
88pub mod clipboard;
89pub mod font;
90/// Shared cell/surface pixel geometry ([`CellGeometry`](geometry::CellGeometry)).
91pub mod geometry;
92/// Canonical default colors ([`DEFAULT_FG`](palette::DEFAULT_FG),
93/// [`DEFAULT_BG`](palette::DEFAULT_BG)) shared by the graphical backends.
94pub mod palette;
95/// The [`Presenter`] trait and [`WindowHandle`](presenter::WindowHandle).
96pub mod presenter;
97#[cfg(feature = "tilesets")]
98pub mod sprite_cache;
99#[cfg(feature = "tilesets")]
100pub mod tileset;
101/// Locates winit's `<canvas>` element via the DOM ([`web::winit_canvas`]).
102#[cfg(target_arch = "wasm32")]
103pub mod web;
104/// The winit event loop, event translation, and app drivers.
105#[cfg(feature = "winit")]
106pub mod winit;
107
108// Compile the code blocks in this crate's own README as doctests so its quick start is
109// type-checked on every test run and cannot silently rot. The `cfg(doctest)` gate keeps this out
110// of the rendered crate documentation -- see `retroglyph-crossterm`'s matching include for the
111// same pattern applied to the workspace root README.
112#[cfg(doctest)]
113#[doc = include_str!("../README.md")]
114struct ReadmeDoctests;
115
116pub use backend::WindowBackend;
117#[cfg(not(target_arch = "wasm32"))]
118pub use clipboard::SystemClipboard;
119pub use clipboard::{Clipboard, ClipboardError};
120pub use geometry::CellGeometry;
121pub use presenter::{GenericSurfaceError, Presenter, RecoverableError, WindowHandle};
122
123// Re-exported so presenters can name the handle traits without adding their
124// own raw-window-handle dependency (and so versions can't drift apart).
125pub use raw_window_handle;