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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Windows, layers, navigation, buttons and drawing.
//!
//! # Windows and layouts
//!
//! The screen shows the top window of a stack. A [`Window<C, D>`](Window) owns two things:
//!
//! - `D`, optional data that lives as long as the window does.
//! - `C`, its [`Layout`]: the layers and any state needed only while the window is loaded. The
//! firmware loads a window when it's pushed and unloads it when it leaves the stack, and gneiss
//! builds `C` in [`Layout::load`] and drops it after [`Layout::unload`] to match.
//!
//! [`Layout::appear`] and [`Layout::disappear`] run whenever the window comes on or goes off
//! screen, including when another window is pushed over it or popped off it.
//!
//! ```no_run
//! # use gneiss::ui::*;
//! struct Counter {
//! text: TextLayer<TextBuf>,
//! count: u32,
//! }
//!
//! impl Layout for Counter {
//! fn load(ctx: &mut WindowCtx<'_, Self>) -> Self {
//! let mut root = ctx.root();
//! let mut text = TextLayer::new(root.bounds()).expect("text layer");
//! text.set_text("0");
//! root.add_child(&mut text);
//! Self { text, count: 0 }
//! }
//! }
//! ```
//!
//! # Layers
//!
//! A layout owns its layers, and each layer owns what it shows: a [`TextLayer`] its text and font,
//! a [`BitmapLayer`] its bitmap, and a [`DataLayer<T>`](DataLayer) a `T` that your [`DrawFn`]
//! draws. Dropping the layout drops them all, and nothing the firmware still points at is freed
//! first. Layer setters return `&mut Self`, so they chain.
//!
//! A layer you never touch again after `load` still has to live in a field, so the layout keeps it
//! alive. Name such fields with a leading underscore (`_logo`) so Rust doesn't warn that they're
//! never read.
//!
//! # Where windows live
//!
//! A window that has to outlive `main` lives in a [`StaticWindow`], or inside another window's
//! `D`. A layout can't own a window ([`NoWindow`]): layouts are dropped when their window
//! unloads, which happens partway through navigation.
//!
//! # Buttons
//!
//! [`Window::set_click_handler`] gives one button a closure, subscribed to the [`ClickKinds`] it
//! wants. It runs with the layout, the window's data, the [`Click`] that happened, and a [`Nav`].
//! Back pops the window unless you give it a handler.
//!
//! # Navigation
//!
//! Pushing or popping a window fires callbacks straight away: the old window's `disappear`, maybe
//! its `unload`, then the new one's `load` and `appear`. From inside a callback that would hand out
//! a second `&mut` to a layout that's still in use, so navigation is queued on a [`Nav`] and runs
//! once the callback returns. [`Nav::push_with`] also hands the pushed window an argument, which its
//! next `appear` receives.
//!
//! # The UI guard
//!
//! [`UiGuard`] is the token behind those rules. At most one exists, every window callback holds it
//! while it runs, and anything that reaches into a window takes it as an argument. `main` is given
//! one; a service handler takes its own with [`UiGuard::enter`].
//!
//! # Updating the screen from a service
//!
//! A service handler reaches a window through its [`StaticWindow`]: [`StaticWindow::with`] lends
//! the layout, if the window is loaded, and the window's data. [`StatusBoard`] is a ready-made
//! window for showing a few lines of status this way.
//!
//! # Drawing
//!
//! [`graphics`] draws with the firmware's own primitives through
//! [`GContext`](graphics::GContext), or with `embedded-graphics` straight into the framebuffer.
use Cell;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
;
// SAFETY: PebbleOS runs all app code on a single task.
// Not an atomic: read-modify-write atomics spin forever!
unsafe
static UI_HELD: UiHeld = UiHeld;
/// Permission to touch the UI: borrowing a layout, changing click handlers, navigating. At most one
/// exists at a time, and every window callback holds one while it runs, so a `&mut UiGuard` proves
/// no other code holds a borrow into any window, and a shared `&UiGuard` proves none is held
/// mutably.
///
/// Some window-related actions can cause reentrant firing of handlers if they're caused from within
/// one. This causes `&mut` aliasing UB if it happens, so this type is used as a proof of "good
/// behaviour", panicking instead of permitting the UB to occur if reentrance is triggered:
/// ```text
/// _appear(win_a)
/// let a1 = &mut A ───────────────────────┐ 'a1
/// A::appear(a1) │
/// window_stack_pop() │
/// _disappear(win_a) │
/// let a2 = &mut A ────────┐ 'a2 │ 'a2 inside 'a1: two live &mut A, UB.
/// A::disappear(a2) │ │ UiGuard::enter turns this into a panic
/// ◄─────────────────────────┘ │
/// _unload(win_a) │
/// drop(A) │ a1 now dangles
/// a1.foo = 1 ◄────────────────────────┘ write to freed A
/// ```
/// Actions that can cause the firing of handlers, or would be rendered unsound by it happening, take
/// this guard as a proof token.
///
/// Holding the token does not put you at risk of panic when an event happens: there's no
/// preemption. Only reentrance will trigger it.
///
/// Forgetting or otherwise leaking a guard makes every later window callback panic.
);