zng/
app.rs

1//! App extensions, context, events and commands API.
2//!
3//! # Runtime
4//!
5//! A typical app instance has two processes, the initial process called the *app-process*, and a second process called the
6//! *view-process*. The app-process implements the event loop and updates, the view-process implements the platform integration and
7//! renderer, the app-process controls the view-process, most of the time app implementers don't interact directly with it, except
8//! at the start where the view-process is spawned.
9//!
10//! The reason for this dual process architecture is mostly for resilience, the unsafe interactions with the operating system and
11//! graphics driver are isolated in a different process, in case of crashes the view-process is respawned automatically and
12//! all windows are recreated. It is possible to run the app in a single process, in this case the view runs in the main thread
13//! and the app main loop in another.
14//!
15//! ## View-Process
16//!
17//! To simplify distribution the view-process is an instance of the same app executable, the view-process crate injects
18//! their own "main" in the [`zng::env::init!`] call, automatically taking over the process if the executable spawns as a view-process.
19//!
20//! On the first instance of the app executable the `init` only inits the env and returns, the app init spawns a second process
21//! marked as the view-process, on this second instance the init call never returns, for this reason the init
22//! must be called early in main, all code before the `init` call runs in both the app and view processes.
23//!
24//! ```toml
25//! [dependencies]
26//! zng = { version = "0.14.2", features = ["view_prebuilt"] }
27//! ```
28//!
29//! ```no_run
30//! use zng::prelude::*;
31//!
32//! fn main() {
33//!     app_and_view();
34//!     zng::env::init!(); // init only returns if it is not called in the view-process.
35//!     app();
36//! }
37//!
38//! fn app_and_view() {
39//!     // code here runs in the app-process and view-process.
40//! }
41//!
42//! fn app() {
43//!     // code here only runs in the app-process.
44//!
45//!     APP.defaults().run(async {
46//!         // ..
47//!     })
48//! }
49//! ```
50//!
51//! ## Same Process
52//!
53//! You can also run the view in the same process, this mode of execution is slightly more efficient, but
54//! your app will not be resilient to crashes caused by the operating system or graphics driver, the app code
55//! will also run in a different thread, not the main.
56//!
57//! ```no_run
58//! use zng::prelude::*;
59//!
60//! fn main() {
61//!     zng::env::init!();
62//!     zng::view_process::prebuilt::run_same_process(app);
63//! }
64//!
65//! fn app() {
66//!     // code here runs in a different thread, the main thread becomes the view.
67//!     APP.defaults().run(async {
68//!         // ..
69//!     })
70//! }
71//! ```
72//!
73//! Note that you must still call `init!` as it also initializes the app metadata and directories.
74//!
75//! # Headless
76//!
77//! The app can also run *headless*, where no window is actually created, optionally with real rendering.
78//! This mode is useful for running integration tests, or for rendering images.
79//!
80//! ```
81//! use zng::prelude::*;
82//!
83//! let mut app = APP.defaults().run_headless(/* with_renderer: */ false);
84//! app.run_window(async {
85//!     Window! {
86//!         child = Text!("Some text");
87//!         auto_size = true;
88//!
89//!         render_mode = window::RenderMode::Software;
90//!         frame_capture_mode = window::FrameCaptureMode::Next;
91//!
92//!         on_frame_image_ready = async_hn!(|args: window::FrameImageReadyArgs| {
93//!             if let Some(img) = args.frame_image {
94//!                 // if the app runs with `run_headless(/* with_renderer: */ true)` an image is captured
95//!                 // and saved here.
96//!                 img.save("screenshot.png").await.unwrap();
97//!             }
98//!
99//!             // close the window, causing the app to exit.
100//!             WINDOW.close();
101//!         });
102//!     }
103//! });
104//! ```
105//!
106//! You can also run multiple headless apps in the same process, one per thread, if the crate is build using the `"multi_app"` feature.
107//!
108//! # App Extension
109//!
110//! Apps can be extended to provide new services and events, in fact all default services and events are implemented as extensions
111//! loaded by [`APP.defaults()`]. The app extension API is [`AppExtension`]. Usually extensions are named with suffix `Manager`, but
112//! that is not a requirement.
113//!
114//! ```
115//! use zng::{app::{AppExtended, AppExtension}, APP};
116//!
117//! #[derive(Default)]
118//! pub struct HelloManager {}
119//! impl AppExtension for HelloManager {
120//!     fn init(&mut self) {
121//!         println!("Hello init!");
122//!     }
123//!
124//!     fn update_preview(&mut self) {
125//!         println!("Hello before UI!");
126//!     }
127//!
128//!     fn update(&mut self) {
129//!         println!("Hello after UI!");
130//!     }
131//! }
132//!
133//! pub fn app() -> AppExtended<impl AppExtension> {
134//!     APP.defaults().extend(HelloManager::default())
135//! }
136//! ```
137//!
138//! ## Services
139//!
140//! App services are defined by convention, there is no service trait or struct. Proper service implementations follow
141//! these rules:
142//!
143//! #### App services are an unit struct named like a static
144//!
145//! This is because services are a kind of *singleton*. The service API is implemented as methods on the service struct.
146//!
147//! ```
148//! # use zng::var::*;
149//! #[expect(non_camel_case_types)]
150//! pub struct SCREAMING_CASE;
151//! impl SCREAMING_CASE {
152//!     pub fn state(&self) -> impl Var<bool> {
153//! #       var(true)
154//!     }
155//! }
156//! ```
157//!
158//! Note that you need to suppress a lint if the service name has more then one word.
159//!
160//! Service state and config methods should prefer variables over direct values. The use of variables allows the service state
161//! to be plugged directly into the UI. Async operations should prefer using [`ResponseVar<R>`] over `async` methods for
162//! the same reason.
163//!
164//! #### App services lifetime is the current app lifetime
165//!
166//! Unlike a simple singleton app services must only live for the duration of the app and must support
167//! multiple parallel instances if built with the `"multi_app"` feature. You can use private
168//! [`app_local!`] static variables as backing storage to fulfill this requirement.
169//!
170//! A common pattern in the zng services is to name the app locals with a `_SV` suffix.
171//!
172//! Services do not expose the app local locking, all state output is cloned the state is only locked
173//! for the duration of the service method call.
174//!
175//! #### App services don't change public state mid update
176//!
177//! All widgets using the service during the same update see the same state. State change requests are scheduled
178//! for the next update, just like variable updates or event notifications. Services also request
179//! an [`UPDATES.update`] after scheduling to wake-up the app in case the service request was made from a [`task`] thread.
180//!
181//! This is even true for the [`INSTANT`] service, although this can be configured for this service using [`APP.pause_time_for_update`].
182//!
183//! [`APP.pause_time_for_update`]: zng_app::APP::pause_time_for_update
184//!
185//! ### Examples
186//!
187//! Fulfilling service requests is where the [`AppExtension`] comes in, it is possible to declare a simple standalone
188//! service using only variables, `Event::on_event` and `UPDATES.run_hn_once`, but an app extension is more efficient
189//! and more easy to implement.
190//!
191//! If the service request can fail or be delayed it is common for the request method to return a [`ResponseVar<R>`]
192//! that is updated once the request is finished. You can also make the method `async`, but a response var is superior
193//! because it can be plugged directly into any UI property, and it can still be awaited using the variable async methods.
194//!
195//! If the service request cannot fail and it is guaranteed to affect an observable change in the service state in the
196//! next update a response var is not needed.
197//!
198//! The example below demonstrates an app extension implementation that provides a service.
199//!
200//! ```
201//! use zng::{prelude_wgt::*, app::AppExtension};
202//!
203//! /// Foo service.
204//! pub struct FOO;
205//!
206//! impl FOO {
207//!     /// Foo read-write var.
208//!     pub fn config(&self) -> impl Var<bool> {
209//!         FOO_SV.read().config.clone()
210//!     }
211//!
212//!     /// Foo request.
213//!     pub fn request(&self, request: char) -> ResponseVar<char> {
214//!         UPDATES.update(None);
215//!
216//!         let mut foo = FOO_SV.write();
217//!         let (responder, response) = response_var();
218//!         foo.requests.push((request, responder));
219//!         response
220//!     }
221//! }
222//!
223//! struct FooService {
224//!     config: ArcVar<bool>,
225//!     requests: Vec<(char, ResponderVar<char>)>,
226//! }
227//!
228//! app_local! {
229//!     static FOO_SV: FooService = FooService { config: var(false), requests: vec![] };
230//! }
231//!
232//! /// Foo app extension.
233//! ///
234//! /// # Services
235//! ///
236//! /// Services provided by this extension.
237//! ///
238//! /// * [`FOO`]
239//! #[derive(Default)]
240//! pub struct FooManager { }
241//!
242//! impl AppExtension for FooManager {
243//!     fn update(&mut self) {
244//!         let mut foo = FOO_SV.write();
245//!
246//!         if let Some(cfg) = foo.config.get_new() {
247//!             println!("foo cfg={cfg}");
248//!         }
249//!
250//!         for (request, responder) in foo.requests.drain(..) {
251//!             println!("foo request {request:?}");
252//!             responder.respond(request);
253//!         }
254//!     }
255//! }
256//! ```
257//!
258//! Note that in the example requests are processed in the [`AppExtension::update`] update that is called
259//! after all widgets have had a chance to make requests. Requests can also be made from parallel [`task`] threads so
260//! the service also requests an [`UPDATES.update`] just in case there is no update running. If you expect to receive many
261//! requests from parallel tasks you can also process requests in the [`AppExtension::update`] instead, but there is probably
262//! little practical difference.
263//!
264//! # Init & Main Loop
265//!
266//! A headed app initializes in this sequence:
267//!
268//! 1. [`AppExtension::register`] is called.
269//! 2. [`AppExtension::enable_device_events`] is queried.
270//! 3. Spawn view-process.
271//! 4. [`AppExtension::init`] is called.
272//! 5. Schedule the app run future to run in the first preview update.
273//! 6. Does [updates loop](#updates-loop).
274//! 7. Does [update events loop](#update-events-loop).
275//! 6. Does [main loop](#main-loop).
276//!
277//! #### Main Loop
278//!
279//! The main loop coordinates view-process events, timers, app events and updates. There is no scheduler, update and event requests
280//! are captured and coalesced to various buffers that are drained in known sequential order. App extensions update one at a time
281//! in the order they are registered. Windows and widgets update in parallel by default, this is controlled by [`WINDOWS.parallel`] and [`parallel`].
282//!
283//! 1. Sleep if there are not pending events or updates.
284//!    * If the view-process is busy blocks until it sends a message, this is a mechanism to stop the app-process
285//!      from overwhelming the view-process.
286//!    * Block until a message is received, from the view-process or from other app threads.
287//!    * If there are [`TIMERS`] or [`VARS`] animations the message block has a deadline to the nearest timer or animation frame.
288//!        * Animations have a fixed frame-rate defined in [`VARS.frame_duration`], it is 60 frames-per-second by default.
289//! 2. Calls elapsed timer handlers.
290//! 3. Calls elapsed animation handlers.
291//!     * These handlers mostly just request var updates are applied in the updates loop.
292//! 4. Does a [view events loop](#view-events-loop).
293//! 4. Does an [updates loop](#updates-loop).
294//! 5. Does an [update events loop](#update-events-loop).
295//! 6. If the view-process is not busy does a [layout loop and render](#layout-loop-and-render).
296//! 7. If exit was requested and not cancelled breaks the loop.
297//!     * Exit is requested automatically when the last open window closes, this is controlled by [`WINDOWS.exit_on_last_close`].
298//!     * Exit can also be requested using [`APP.exit`].
299//!
300//! #### View Events Loop
301//!
302//! All pending events send by the view-process are coalesced and notify sequentially.
303//!
304//! 1. For each event in the received order (FIFO) that converts to a RAW event.
305//!     1. Calls [`AppExtension::event_preview`].
306//!     2. Calls [`Event::on_pre_event`] handlers.
307//!     3. Calls [`AppExtension::event_ui`].
308//!         * Raw events don't target any widget, but widgets can subscribe, subscribers receive the event in parallel by default.
309//!     4. Calls [`AppExtension::event`].
310//!     5. Calls [`Event::on_event`] handlers.
311//!     6. Does an [updates loop](#updates-loop).
312//! 2. Frame rendered raw event.
313//!     * Same notification sequence as other view-events, just delayed.
314//!
315//! #### Updates Loop
316//!
317//! The updates loop rebuilds info trees if needed , applies pending variable updates and hooks and collects event updates
318//! requested by the app.
319//!
320//! 1. Takes info rebuild request flag.
321//!     * Calls [`AppExtension::info`] if needed.
322//!     * Windows and widgets that requested info (re)build are called.
323//!     * Info rebuild happens in parallel by default.
324//! 2. Takes events and updates requests.
325//!     1. Event hooks are called for new event requests.
326//!         * Full event notification is delayed to after the updates loop.
327//!     2. [var updates loop](#var-updates-loop)
328//!     3. Calls [`AppExtension::update_preview`] if any update was requested.
329//!     4. Calls [`UPDATES.on_pre_update`] handlers if needed.
330//!     5. Calls [`AppExtension::update_ui`] if any update was requested.
331//!         * Windows and widgets that requested update receive it here.
332//!         * All the pending updates are processed in one pass, all targeted widgets are visited once, in parallel by default.
333//!     6. Calls [`AppExtension::update`] if any update was requested.
334//!     7. Calls [`UPDATES.on_update`] handlers if needed.
335//! 3. The loop repeats immediately if any info rebuild or update was requested by update callbacks.
336//!     * The loops breaks if it repeats over 1000 times.
337//!     * An error is logged with a trace the most frequent sources of update requests.
338//!
339//! #### Var Updates Loop
340//!
341//! The variable updates loop applies pending modifications, calls hooks to update variable and bindings.
342//!
343//! 1. Pending variable modifications are applied.
344//! 2. Var hooks are called.
345//!     * The mapping and binding mechanism is implemented using hooks.
346//! 3. The loop repeats until hooks have stopped modifying variables.
347//!     * The loop breaks if it repeats over 1000 times.
348//!     * An error is logged if this happens.
349//!
350//! #### Update Events Loop
351//!
352//! The update events loop notifies each event raised by the app code during previous updates.
353//!
354//! 1. For each event in the request order (FIFO).
355//!     1. Calls [`AppExtension::event_preview`].
356//!     2. Calls [`Event::on_pre_event`] handlers.
357//!     3. Calls [`AppExtension::event_ui`].
358//!         * Windows and widgets targeted by the event update receive it here.
359//!         * If the event targets multiple widgets they receive it in parallel by default.
360//!     4. Calls [`AppExtension::event`].
361//!     5. Calls [`Event::on_event`] handlers.
362//!     6. Does an [updates loop](#updates-loop).
363//!
364//! #### Layout Loop and Render
365//!
366//! Layout and render requests are coalesced, multiple layout requests for the same widget update it once, multiple
367//! render requests become one frame, and if both render and render_update are requested for a window it will fully render.
368//!
369//! 1. Take layout and render requests.
370//! 2. Layout loop.
371//!     1. Calls [`AppExtension::layout`].
372//!         * Windows and widgets that requested layout update in parallel by default.
373//!     2. Does an [updates loop](#updates-loop).
374//!     3. Take layout and render requests, the loop repeats immediately if layout was requested again.
375//!         * The loop breaks if it repeats over 1000 times.
376//!         * An error is logged with a trace the most frequent sources of update requests.
377//! 3. If render was requested, calls [`AppExtension::render`].
378//!     * Windows and widgets that requested render (or render_update) do know in parallel by default.
379//!     * The render pass updates widget transforms and hit-test, generates a display list and sends it to the view-process.
380//!
381//! [`APP.defaults()`]: crate::APP::defaults
382//! [`UPDATES.update`]: crate::update::UPDATES::update
383//! [`task`]: crate::task
384//! [`ResponseVar<R>`]: crate::var::ResponseVar
385//! [`TIMERS`]: crate::timer::TIMERS
386//! [`VARS`]: crate::var::VARS
387//! [`VARS.frame_duration`]: crate::var::VARS::frame_duration
388//! [`WINDOWS.parallel`]: crate::window::WINDOWS::parallel
389//! [`parallel`]: fn@crate::widget::parallel
390//! [`UPDATES.on_pre_update`]: crate::update::UPDATES::on_pre_update
391//! [`UPDATES.on_update`]: crate::update::UPDATES::on_update
392//! [`Event::on_pre_event`]: crate::event::Event::on_pre_event
393//! [`Event::on_event`]: crate::event::Event::on_event
394//! [`WINDOWS.exit_on_last_close`]: crate::window::WINDOWS::exit_on_last_close
395//! [`APP.exit`]: crate::APP#method.exit
396//!
397//! # Full API
398//!
399//! This module provides most of the app API needed to make and extend apps, some more advanced or experimental API
400//! may be available at the [`zng_app`], [`zng_app_context`] and [`zng_ext_single_instance`] base crates.
401
402pub use zng_app::{
403    AppControlFlow, AppEventObserver, AppExtended, AppExtension, AppExtensionBoxed, AppExtensionInfo, AppStartArgs, DInstant, Deadline,
404    EXIT_CMD, EXIT_REQUESTED_EVENT, ExitRequestedArgs, HeadlessApp, INSTANT, InstantMode, on_app_start, print_tracing,
405    print_tracing_filter,
406};
407
408#[cfg(feature = "test_util")]
409pub use zng_app::test_log;
410
411pub use zng_app_context::{
412    AppId, AppLocal, AppScope, CaptureFilter, ContextLocal, ContextValueSet, LocalContext, MappedRwLockReadGuardOwned,
413    MappedRwLockWriteGuardOwned, ReadOnlyRwLock, RunOnDrop, RwLockReadGuardOwned, RwLockWriteGuardOwned, app_local, context_local,
414};
415pub use zng_wgt_input::cmd::{
416    NEW_CMD, OPEN_CMD, SAVE_AS_CMD, SAVE_CMD, on_new, on_open, on_pre_new, on_pre_open, on_pre_save, on_pre_save_as, on_save, on_save_as,
417};
418
419pub use zng_app::view_process::raw_events::{LOW_MEMORY_EVENT, LowMemoryArgs};
420
421/// Input device hardware ID and events.
422///
423/// # Full API
424///
425/// See [`zng_app::view_process::raw_device_events`] for the full API.
426pub mod raw_device_events {
427    pub use zng_app::view_process::raw_device_events::{
428        DEVICE_ADDED_EVENT, DEVICE_REMOVED_EVENT, DeviceArgs, DeviceId, MOTION_EVENT, MotionArgs,
429    };
430}
431
432#[cfg(single_instance)]
433pub use zng_ext_single_instance::{APP_INSTANCE_EVENT, AppInstanceArgs};
434
435/// App-process crash handler.
436///
437/// In builds with `"crash_handler"` feature the crash handler takes over the first "app-process" turning it into
438/// the monitor-process, it spawns another process that is the monitored app-process. If the app-process crashes
439/// the monitor-process spawns a dialog-process that calls the dialog handler to show an error message, upload crash reports, etc.
440///
441/// The dialog handler can be set using [`crash_handler_config!`].
442///
443/// [`crash_handler_config!`]: crate::app::crash_handler::crash_handler_config
444///
445/// # Examples
446///
447/// The example below demonstrates an app setup to show a custom crash dialog.
448///
449/// ```no_run
450/// use zng::prelude::*;
451///
452/// fn main() {
453///     // tracing applied to all processes.
454///     zng::app::print_tracing(tracing::Level::INFO);
455///
456///     // monitor-process spawns app-process and if needed dialog-process here.
457///     zng::env::init!();
458///
459///     // app-process:
460///     app_main();
461/// }
462///
463/// fn app_main() {
464///     APP.defaults().run_window(async {
465///         Window! {
466///             child_align = Align::CENTER;
467///             child = Stack! {
468///                 direction = StackDirection::top_to_bottom();
469///                 spacing = 5;
470///                 children = ui_vec![
471///                     Button! {
472///                         child = Text!("Crash (panic)");
473///                         on_click = hn_once!(|_| {
474///                             panic!("Test panic!");
475///                         });
476///                     },
477///                     Button! {
478///                         child = Text!("Crash (access violation)");
479///                         on_click = hn_once!(|_| {
480///                             // SAFETY: deliberate access violation
481///                             #[expect(deref_nullptr)]
482///                             unsafe {
483///                                 *std::ptr::null_mut() = true;
484///                             }
485///                         });
486///                     }
487///                 ]
488///             };
489///         }
490///     });
491/// }
492///
493/// zng::app::crash_handler::crash_handler_config!(|cfg| {
494///     // monitor-process and dialog-process
495///
496///     cfg.dialog(|args| {
497///         // dialog-process
498///         APP.defaults().run_window(async move {
499///             Window! {
500///                 title = "App Crashed!";
501///                 auto_size = true;
502///                 min_size = (300, 100);
503///                 start_position = window::StartPosition::CenterMonitor;
504///                 on_load = hn_once!(|_| WINDOW.bring_to_top());
505///                 padding = 10;
506///                 child = Text!(args.latest().message());
507///                 child_bottom = Stack! {
508///                     direction = StackDirection::start_to_end();
509///                     layout::align = Align::BOTTOM_END;
510///                     spacing = 5;
511///                     children = ui_vec![
512///                         Button! {
513///                             child = Text!("Restart App");
514///                             on_click = hn_once!(args, |_| {
515///                                 args.restart();
516///                             });
517///                         },
518///                         Button! {
519///                             child = Text!("Exit App");
520///                             on_click = hn_once!(|_| {
521///                                 args.exit(0);
522///                             });
523///                         },
524///                     ]
525///                 }, 10;
526///             }
527///         });
528///     });
529/// });
530///
531/// ```
532///
533/// # Debugger
534///
535/// Note that because the crash handler spawns a different process for the app debuggers will not
536/// stop at break points in the app code. You can configure your debugger to set the `NO_ZNG_CRASH_HANDLER` environment
537/// variable to not use a crash handler in debug runs.
538///
539/// On VS Code with the CodeLLDB extension you can add this workspace configuration:
540///
541/// ```json
542/// "lldb.launch.env": {
543///    "ZNG_NO_CRASH_HANDLER": ""
544/// }
545/// ```
546///
547/// # Full API
548///
549/// See [`zng_app::crash_handler`] and [`zng_wgt_inspector::crash_handler`] for the full API.
550#[cfg(crash_handler)]
551pub mod crash_handler {
552    pub use zng_app::crash_handler::{BacktraceFrame, CrashArgs, CrashConfig, CrashError, CrashPanic, crash_handler_config};
553
554    #[cfg(feature = "crash_handler_debug")]
555    pub use zng_wgt_inspector::crash_handler::debug_dialog;
556
557    crash_handler_config!(|cfg| {
558        cfg.default_dialog(|args| {
559            if let Some(c) = &args.dialog_crash {
560                eprintln!("DEBUG CRASH DIALOG ALSO CRASHED");
561                eprintln!("   {}", c.message());
562                eprintln!("ORIGINAL APP CRASH");
563                eprintln!("   {}", args.latest().message());
564                args.exit(0xBADC0DE)
565            } else {
566                #[cfg(feature = "crash_handler_debug")]
567                {
568                    use crate::prelude::*;
569                    APP.defaults()
570                        .run_window(async_clmv!(args, { zng_wgt_inspector::crash_handler::debug_dialog(args) }));
571                }
572
573                #[cfg(not(feature = "crash_handler_debug"))]
574                {
575                    eprintln!(
576                        "app crashed {}\n\nbuild with feature = \"crash_handler_debug\" to se the debug crash dialog",
577                        args.latest().message()
578                    );
579                }
580            }
581            args.exit(0)
582        });
583    });
584}