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