Skip to main content

retroglyph_window/
lib.rs

1//! A shared layer for window-based backends (software, GL, wgpu).
2//!
3//! # Architecture
4//!
5//! [`Backend`](retroglyph_core::Backend) fuses input (`poll_event`/
6//! `push_event`) and output (`draw_layers`/`flush`/...), which fits a
7//! terminal process but not a window: there, an event loop owns input and a
8//! renderer owns output. This crate splits the two apart and reassembles
9//! them 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 the output half: rasterization plus the surface
34//!   lifecycle (`init_surface`/`resize_surface`/`present`/`cell_size`).
35//!   Renderer crates implement only this trait.
36//! - <code>[WindowBackend]&lt;P: Presenter&gt;</code> implements `Backend`
37//!   generically, holding the input event queue and delegating output to
38//!   `P`.
39//! - The `winit` module (feature-gated, see below) drives the event loop
40//!   that fills that queue 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
46//! `winit` feature (default on) additionally provides the `winit` module:
47//! the event loop, event translation, and the `run_windowed`/`run_app`
48//! drivers. Disable it to implement or drive `Presenter` with a different
49//! windowing library (SDL2, tao, a custom loop) without pulling in winit.
50//!
51//! # DPI, scale, and the resize contract
52//!
53//! [`Presenter::cell_size`] returns the cell size in **physical pixels** -- the same pixel
54//! space as `winit::dpi::PhysicalSize` -- not logical/DPI-scaled ("CSS" or "point") pixels.
55//! This crate performs no automatic DPI scaling of it: nothing here changes `cell_size()` in
56//! response to a display's scale factor. `SoftwareRenderer`'s cell size, for example, is
57//! fixed at construction (glyph size × its integer `scale` config) and never changes on a
58//! [`Presenter::scale_factor_changed`] notification. A presenter that wants larger cells on a
59//! `HiDPI` display has to opt into that itself from `scale_factor_changed` (e.g. regenerating a
60//! font atlas at a new pixel density); until one does, the grid renders at a fixed physical
61//! pixel size on every display, `HiDPI` or not.
62//!
63//! Window resize is clamped to whole cells: a physical size that isn't an exact multiple of
64//! `cell_size()` has its sub-cell remainder truncated, not centered or cleared, and the OS
65//! window is never resized to compensate -- see [`Presenter::resize_surface`]'s doc comment
66//! for the full contract, including the unpainted trailing strip this can leave on screen.
67//!
68//! # Threading model
69//!
70//! The windowed drivers (`winit::run_windowed`, `winit::run_app`, and their `_with_proxy`
71//! variants) are single-threaded: the event loop, every [`Presenter`] call, and the app
72//! closure/[`App`](retroglyph_core::App) callback all run on the one thread that calls
73//! `run_windowed`/`run_app` -- the main thread, on platforms (e.g. macOS) that require it for
74//! windowing. Neither [`Presenter`] nor [`WindowBackend`] carries a `Send`/`Sync` bound
75//! anywhere in this crate, and a presenter is free to hold thread-affine state accordingly
76//! (an `Rc`, a non-`Send` GPU context handle). The only supported way to reach the loop from
77//! another thread is `winit::EventProxy`, which is `Send + Sync + Clone` but only injects an
78//! opaque `u64` as [`Event::Custom`](retroglyph_core::event::Event::Custom) -- it does not
79//! give another thread direct access to the `Presenter` or `Terminal`.
80
81/// The generic [`Backend`](retroglyph_core::Backend) for windowed presenters.
82pub mod backend;
83/// The [`Presenter`] trait and [`WindowHandle`](presenter::WindowHandle).
84pub mod presenter;
85/// The winit event loop, event translation, and app drivers.
86#[cfg(feature = "winit")]
87pub mod winit;
88
89// Compile the code blocks in this crate's own README as doctests so its quick start is
90// type-checked on every test run and cannot silently rot. The `cfg(doctest)` gate keeps this out
91// of the rendered crate documentation -- see `retroglyph-crossterm`'s matching include for the
92// same pattern applied to the workspace root README.
93#[cfg(doctest)]
94#[doc = include_str!("../README.md")]
95struct ReadmeDoctests;
96
97pub use backend::WindowBackend;
98pub use presenter::{Presenter, WindowHandle};
99
100// Re-exported so presenters can name the handle traits without adding their
101// own raw-window-handle dependency (and so versions can't drift apart).
102pub use raw_window_handle;