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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//! Safe Rust bindings to the Noesis GUI native SDK, a XAML-based UI engine,
//! wrapped here by a hand-written C ABI over opaque, reference-counted handles.
//!
//! The crate is renderer-agnostic: it builds UI trees, drives input, and ticks
//! the layout/animation engine, but leaves drawing to you through the
//! [`render_device::RenderDevice`] trait. A ready-made Bevy/wgpu integration
//! lives in the sibling crate `noesis_bevy`.
//!
//! # Getting started
//!
//! The lifecycle is process-wide: call [`init`] exactly once at startup and
//! [`shutdown`] once at exit, after every Noesis handle has been dropped. In
//! between, a typical session looks like:
//!
//! 1. Install an asset source with [`xaml_provider::set_xaml_provider`] (plus
//! optional font and texture providers).
//! 2. Build a UI root: [`view::FrameworkElement::load`] from a URI, or
//! [`view::FrameworkElement::parse`] from an in-memory XAML string.
//! 3. Wrap it in a [`view::View`] with [`view::View::create`], then feed it the
//! surface size, input events, and per-frame time updates.
//! 4. Render the view through your [`render_device::RenderDevice`].
//!
//! For a quick import of the types most code reaches for, glob the [`prelude`].
//!
//! # Setup
//!
//! Set `NOESIS_SDK_DIR` to the extracted Noesis Native SDK 3.2.13 root (the
//! directory containing `Include/` and `Bin/`). See `README.md`.
//!
//! # Thread affinity
//!
//! Noesis objects are **thread-affine**: the engine expects every method on a
//! given object (and on the [`view::View`] that owns it) to be called from the
//! one thread that drives that view. The owning handle types in this crate
//! (geometries, brushes, transforms, commands, bindings, the RAII registration
//! guards, etc.) therefore implement [`Send`] but deliberately **not** [`Sync`]:
//!
//! - **`Send` is sound.** `Noesis::BaseRefCounted::mRefCount` is an
//! `AtomicInteger`, so the `-1` release performed by [`Drop`] is safe on any
//! thread. Ownership of a handle may be *moved* to whichever thread owns the
//! Noesis view, after which all calls happen on that thread.
//! - **`Sync` is unsound.** Many `&self` methods call into Noesis (FFI reads,
//! and some lazily mutate engine state, e.g. resource-dictionary or
//! collection getters). Sharing a `&handle` across threads would let two
//! threads invoke those concurrently on a thread-affine engine, which races.
//!
//! Callers must invoke Noesis methods only from the view's thread. SAFETY
//! comments on the individual `unsafe impl Send` blocks point back here rather
//! than restating this contract. (The render-device *trait* is the exception:
//! a user [`render_device::RenderDevice`] impl must be `Send + Sync` because
//! Noesis may call its trampolines from a dedicated render thread. That bound
//! is on the trait, not on these owning handles.)
use ;
// Not part of the stable API; no semver guarantees.
pub
/// Optional. Apply Indie license credentials before [`init`] to suppress the
/// trial watermark. Pass empty strings (or skip the call) to run in trial mode.
///
/// # Panics
///
/// Panics if `name` or `key` contain interior NUL bytes.
/// Disable the Hot Reload feature before [`init`]. Hot Reload is on by default
/// in Debug/Profile SDK builds and costs a little extra memory; disabling it is
/// purely an optimization. No-op once [`init`] has run, and a no-op on a
/// Release dylib where the feature is compiled out.
///
/// Part of the inspector / hot-reload control surface (see
/// [`disable_inspector`], [`disable_socket_init`], [`is_inspector_connected`],
/// [`update_inspector`]). There is intentionally no `enable_*` counterpart:
/// these features default on in instrumented SDK builds, so we only expose the
/// off switches plus the runtime queries.
///
/// Must be called **before** [`init`].
/// Skip the Inspector's socket initialization (e.g. `WSAStartup` on Windows)
/// before [`init`]. Use this only when the host process has already initialized
/// sockets itself, to avoid a double init. No-op after [`init`] / on a Release
/// dylib.
///
/// Must be called **before** [`init`].
/// Disable all remote Inspector connections before [`init`]. The Inspector is
/// enabled by default in Debug/Profile SDK builds (it opens a socket and waits
/// for the remote tool); call this to keep it off. No-op after [`init`] / on a
/// Release dylib where the Inspector is compiled out.
///
/// Must be called **before** [`init`].
/// Returns whether a remote Inspector is currently connected.
///
/// Always `false` when nothing is attached, and always `false` on a Release
/// dylib (the Inspector is compiled out of Release SDK builds). The value of
/// exposing it is the query itself plus the [`update_inspector`] pump for hosts
/// running an instrumented build.
/// Keep the Inspector connection alive. [`crate::view::View`] updates call this
/// internally, so it is only needed when the Inspector connects before any view
/// exists. No-op on a Release dylib.
/// Initialize Noesis subsystems. Call exactly once per process; Noesis does
/// not support re-init after [`shutdown`].
/// Shut Noesis down. Call once at process exit, after all Noesis-owned objects
/// have been released.
/// Curated re-exports of the items most code reaches for. Glob-import it
/// (`use noesis_runtime::prelude::*;`) to pull in the core view/element
/// handles, the brush/transform/geometry traits and their common concrete
/// types, data-binding and collection types, the custom-control and
/// markup-extension surface, the provider traits, the lifecycle free functions,
/// and the most-used enums.
///
/// This is a convenience surface, not the full API. Anything not listed here is
/// still reachable through its owning module (`crate::animation`,
/// `crate::input`, `crate::diagnostics`, ...).
///
/// Compiled (so the names stay honest) but not executed: Noesis [`init`] runs
/// once per process, and `cargo test` merges all doctests into one binary.
///
/// ```no_run
/// use noesis_runtime::prelude::*;
///
/// noesis_runtime::init();
/// // Freestanding objects round-trip through the FFI without a live view.
/// let mut items = ObservableCollection::new();
/// assert!(items.is_empty());
/// items.push_string("first");
/// items.push_string("second");
/// assert_eq!(items.len(), 2);
///
/// // Build a brush and read its color back across the FFI boundary.
/// let mut brush = SolidColorBrush::new([1.0, 0.0, 0.0, 1.0]);
/// brush.set_color([0.0, 1.0, 0.0, 1.0]);
/// assert_eq!(brush.color(), [0.0, 1.0, 0.0, 1.0]);
/// noesis_runtime::shutdown();
/// ```
/// Returns the Noesis runtime build version (e.g. `"3.2.13"`).