zng_app/
running.rs

1use std::{
2    collections::HashMap,
3    fmt, mem,
4    path::PathBuf,
5    sync::Arc,
6    task::Waker,
7    time::{Duration, Instant},
8};
9
10use crate::{Deadline, handler::HandlerExt as _, view_process::raw_events::RAW_FRAME_RENDERED_EVENT, window::WINDOWS_APP};
11use parking_lot::Mutex;
12use zng_app_context::{AppScope, app_local};
13use zng_task::DEADLINE_APP;
14use zng_task::channel::{self, ChannelError};
15use zng_time::{INSTANT_APP, InstantMode};
16use zng_txt::Txt;
17use zng_var::{ArcEq, ResponderVar, ResponseVar, VARS_APP, Var, expr_var, response_var};
18use zng_view_api::{DeviceEventsFilter, raw_input::InputDeviceEvent};
19
20use crate::{
21    APP, AppControlFlow, DInstant, INSTANT,
22    event::{CommandInfoExt, CommandNameExt, command, event},
23    event_args,
24    shortcut::CommandShortcutExt,
25    shortcut::shortcut,
26    timer::TimersService,
27    update::{ContextUpdates, InfoUpdates, LayoutUpdates, RenderUpdates, UPDATES, UpdateOp, UpdateTrace, UpdatesTrace, WidgetUpdates},
28    view_process::{raw_device_events::InputDeviceId, *},
29    widget::WidgetId,
30    window::WindowId,
31};
32
33/// Represents a running app controlled by an external event loop.
34pub(crate) struct RunningApp {
35    receiver: channel::Receiver<AppEvent>,
36
37    loop_timer: LoopTimer,
38    loop_monitor: LoopMonitor,
39    last_wait_event: Instant,
40
41    pending_view_events: Vec<zng_view_api::Event>,
42    pending_view_frame_events: Vec<zng_view_api::window::EventFrameRendered>,
43    pending: ContextUpdates,
44
45    exited: bool,
46
47    // cleans on drop
48    _scope: AppScope,
49}
50impl Drop for RunningApp {
51    fn drop(&mut self) {
52        let _s = tracing::debug_span!("RunningApp::drop").entered();
53        APP.call_deinit_handlers();
54        VIEW_PROCESS.exit();
55    }
56}
57impl RunningApp {
58    pub(crate) fn start(
59        scope: AppScope,
60        is_headed: bool,
61        with_renderer: bool,
62        view_process_exe: Option<PathBuf>,
63        view_process_env: HashMap<Txt, Txt>,
64        is_minimal: bool,
65    ) -> Self {
66        let _s = tracing::debug_span!("APP::start").entered();
67
68        let (sender, receiver) = AppEventSender::new();
69
70        UPDATES.init(sender);
71
72        fn app_waker() {
73            UPDATES.update_app();
74        }
75        VARS_APP.init_app_waker(app_waker);
76        VARS_APP.init_modify_trace(UpdatesTrace::log_var);
77        DEADLINE_APP.init_deadline_service(crate::timer::deadline_service);
78        zng_var::animation::TRANSITIONABLE_APP.init_rgba_lerp(zng_color::lerp_rgba);
79
80        if with_renderer && view_process_exe.is_none() {
81            zng_env::assert_inited();
82        }
83
84        #[cfg(not(target_arch = "wasm32"))]
85        let view_process_exe = view_process_exe.unwrap_or_else(|| std::env::current_exe().expect("current_exe"));
86        #[cfg(target_arch = "wasm32")]
87        let view_process_exe = std::path::PathBuf::from("<wasm>");
88
89        APP.pre_init(is_headed, with_renderer, view_process_exe, view_process_env);
90
91        APP.call_init_handlers(is_minimal);
92
93        RunningApp {
94            receiver,
95
96            loop_timer: LoopTimer::default(),
97            loop_monitor: LoopMonitor::default(),
98            last_wait_event: Instant::now(),
99
100            pending_view_events: Vec::with_capacity(100),
101            pending_view_frame_events: Vec::with_capacity(5),
102            pending: ContextUpdates {
103                update: false,
104                info: false,
105                layout: false,
106                render: false,
107                update_widgets: WidgetUpdates::default(),
108                info_widgets: InfoUpdates::default(),
109                layout_widgets: LayoutUpdates::default(),
110                render_widgets: RenderUpdates::default(),
111                render_update_widgets: RenderUpdates::default(),
112            },
113            exited: false,
114
115            _scope: scope,
116        }
117    }
118
119    pub fn has_exited(&self) -> bool {
120        self.exited
121    }
122
123    fn input_device_id(&mut self, id: zng_view_api::raw_input::InputDeviceId) -> InputDeviceId {
124        VIEW_PROCESS.input_device_id(id)
125    }
126
127    /// Process a View Process event.
128    fn on_view_event(&mut self, ev: zng_view_api::Event) {
129        use crate::view_process::raw_device_events::*;
130        use crate::view_process::raw_events::*;
131        use zng_view_api::Event;
132
133        fn window_id(id: zng_view_api::window::WindowId) -> WindowId {
134            WindowId::from_raw(id.get())
135        }
136        fn audio_output_id(id: zng_view_api::audio::AudioOutputId) -> AudioOutputId {
137            AudioOutputId::from_raw(id.get())
138        }
139
140        match ev {
141            Event::MouseMoved {
142                window: w_id,
143                device: d_id,
144                coalesced_pos,
145                position,
146            } => {
147                let args = RawMouseMovedArgs::now(window_id(w_id), self.input_device_id(d_id), coalesced_pos, position);
148                RAW_MOUSE_MOVED_EVENT.notify(args);
149            }
150            Event::MouseEntered {
151                window: w_id,
152                device: d_id,
153            } => {
154                let args = RawMouseArgs::now(window_id(w_id), self.input_device_id(d_id));
155                RAW_MOUSE_ENTERED_EVENT.notify(args);
156            }
157            Event::MouseLeft {
158                window: w_id,
159                device: d_id,
160            } => {
161                let args = RawMouseArgs::now(window_id(w_id), self.input_device_id(d_id));
162                RAW_MOUSE_LEFT_EVENT.notify(args);
163            }
164            Event::WindowChanged(c) => {
165                let monitor_id = c.monitor.map(|id| VIEW_PROCESS.monitor_id(id));
166                let mut args = RawWindowChangedArgs::now(
167                    window_id(c.window),
168                    c.state,
169                    c.position,
170                    monitor_id,
171                    c.size,
172                    c.safe_padding,
173                    c.cause,
174                    c.frame_wait_id,
175                );
176                args.scale_factor = c.scale_factor;
177                args.refresh_rate = c.refresh_rate;
178                RAW_WINDOW_CHANGED_EVENT.notify(args);
179            }
180            Event::DragHovered { window, data, allowed } => {
181                let args = RawDragHoveredArgs::now(window_id(window), data, allowed);
182                RAW_DRAG_HOVERED_EVENT.notify(args);
183            }
184            Event::DragMoved {
185                window,
186                coalesced_pos,
187                position,
188            } => {
189                let args = RawDragMovedArgs::now(window_id(window), coalesced_pos, position);
190                RAW_DRAG_MOVED_EVENT.notify(args);
191            }
192            Event::DragDropped {
193                window,
194                data,
195                allowed,
196                drop_id,
197            } => {
198                let args = RawDragDroppedArgs::now(window_id(window), data, allowed, drop_id);
199                RAW_DRAG_DROPPED_EVENT.notify(args);
200            }
201            Event::DragCancelled { window } => {
202                let args = RawDragCancelledArgs::now(window_id(window));
203                RAW_DRAG_CANCELLED_EVENT.notify(args);
204            }
205            Event::AppDragEnded { window, drag, applied } => {
206                let args = RawAppDragEndedArgs::now(window_id(window), drag, applied);
207                RAW_APP_DRAG_ENDED_EVENT.notify(args);
208            }
209            Event::FocusChanged { prev, new } => {
210                let args = RawWindowFocusArgs::now(prev.map(window_id), new.map(window_id));
211                RAW_WINDOW_FOCUS_EVENT.notify(args);
212            }
213            Event::KeyboardInput {
214                window: w_id,
215                device: d_id,
216                key_code,
217                state,
218                key,
219                key_location,
220                key_modified,
221                text,
222            } => {
223                let args = RawKeyInputArgs::now(
224                    window_id(w_id),
225                    self.input_device_id(d_id),
226                    key_code,
227                    key_location,
228                    state,
229                    key,
230                    key_modified,
231                    text,
232                );
233                RAW_KEY_INPUT_EVENT.notify(args);
234            }
235            Event::Ime { window: w_id, ime } => {
236                let args = RawImeArgs::now(window_id(w_id), ime);
237                RAW_IME_EVENT.notify(args);
238            }
239
240            Event::MouseWheel {
241                window: w_id,
242                device: d_id,
243                delta,
244                phase,
245            } => {
246                let args = RawMouseWheelArgs::now(window_id(w_id), self.input_device_id(d_id), delta, phase);
247                RAW_MOUSE_WHEEL_EVENT.notify(args);
248            }
249            Event::MouseInput {
250                window: w_id,
251                device: d_id,
252                state,
253                button,
254            } => {
255                let args = RawMouseInputArgs::now(window_id(w_id), self.input_device_id(d_id), state, button);
256                RAW_MOUSE_INPUT_EVENT.notify(args);
257            }
258            Event::TouchpadPressure {
259                window: w_id,
260                device: d_id,
261                pressure,
262                stage,
263            } => {
264                let args = RawTouchpadPressureArgs::now(window_id(w_id), self.input_device_id(d_id), pressure, stage);
265                RAW_TOUCHPAD_PRESSURE_EVENT.notify(args);
266            }
267            Event::AxisMotion {
268                window: w_id,
269                device: d_id,
270                axis,
271                value,
272            } => {
273                let args = RawAxisMotionArgs::now(window_id(w_id), self.input_device_id(d_id), axis, value);
274                RAW_AXIS_MOTION_EVENT.notify(args);
275            }
276            Event::Touch {
277                window: w_id,
278                device: d_id,
279                touches,
280            } => {
281                let args = RawTouchArgs::now(window_id(w_id), self.input_device_id(d_id), touches);
282                RAW_TOUCH_EVENT.notify(args);
283            }
284            #[allow(deprecated)]
285            Event::ScaleFactorChanged {
286                monitor: id,
287                windows,
288                scale_factor,
289            } => {
290                let monitor_id = VIEW_PROCESS.monitor_id(id);
291                let windows: Vec<_> = windows.into_iter().map(window_id).collect();
292                let args = RawScaleFactorChangedArgs::now(monitor_id, windows, scale_factor);
293                RAW_SCALE_FACTOR_CHANGED_EVENT.notify(args);
294            }
295            Event::MonitorsChanged(monitors) => {
296                let monitors: Vec<_> = monitors.into_iter().map(|(id, info)| (VIEW_PROCESS.monitor_id(id), info)).collect();
297                let args = RawMonitorsChangedArgs::now(monitors);
298                RAW_MONITORS_CHANGED_EVENT.notify(args);
299            }
300            Event::AudioDevicesChanged(_audio_devices) => {}
301            Event::WindowCloseRequested(w_id) => {
302                let args = RawWindowCloseRequestedArgs::now(window_id(w_id));
303                RAW_WINDOW_CLOSE_REQUESTED_EVENT.notify(args);
304            }
305            Event::WindowOpened(w_id, data) => {
306                let w_id = window_id(w_id);
307                let (window, data) = VIEW_PROCESS.on_window_opened(w_id, data);
308                let args = RawWindowOpenArgs::now(w_id, window.downgrade(), data);
309                RAW_WINDOW_OPEN_EVENT.notify(args);
310                UPDATES.once_next_update("", move || {
311                    let _hold_once = &window;
312                });
313            }
314            Event::HeadlessOpened(w_id, data) => {
315                let w_id = window_id(w_id);
316                let (surface, data) = VIEW_PROCESS.on_headless_opened(w_id, data);
317                let args = RawHeadlessOpenArgs::now(w_id, surface.downgrade(), data);
318                RAW_HEADLESS_OPEN_EVENT.notify(args);
319                UPDATES.once_next_update("", move || {
320                    let _hold_once = &surface;
321                });
322            }
323            Event::WindowOrHeadlessOpenError { id: w_id, error } => {
324                let w_id = window_id(w_id);
325                let args = RawWindowOrHeadlessOpenErrorArgs::now(w_id, error);
326                RAW_WINDOW_OR_HEADLESS_OPEN_ERROR_EVENT.notify(args);
327            }
328            Event::WindowClosed(w_id) => {
329                let args = RawWindowCloseArgs::now(window_id(w_id));
330                RAW_WINDOW_CLOSE_EVENT.notify(args);
331            }
332            Event::ImageMetadataDecoded(meta) => {
333                if let Some(handle) = VIEW_PROCESS.on_image_metadata(&meta) {
334                    let args = RawImageMetadataDecodedArgs::now(handle.downgrade(), meta);
335                    RAW_IMAGE_METADATA_DECODED_EVENT.notify(args);
336                    UPDATES.once_next_update("", move || {
337                        let _hold_once = &handle;
338                    });
339                } else {
340                    tracing::warn!("received unknown image metadata {:?} ({:?}), ignoring", meta.id, meta.size);
341                }
342            }
343            Event::ImageDecoded(img) => {
344                if let Some(handle) = VIEW_PROCESS.on_image_decoded(&img) {
345                    let img = ArcEq::new(img);
346                    let args = RawImageDecodedArgs::now(handle.downgrade(), ArcEq::downgrade(&img));
347                    RAW_IMAGE_DECODED_EVENT.notify(args);
348                    UPDATES.once_next_update("", move || {
349                        let _hold_once = (&handle, &img);
350                    });
351                } else {
352                    tracing::warn!("received unknown image data {:?} ({:?}), ignoring", img.meta.id, img.meta.size);
353                }
354            }
355            Event::ImageDecodeError { image: id, error } => {
356                if let Some(handle) = VIEW_PROCESS.on_image_error(id) {
357                    let args = RawImageDecodeErrorArgs::now(handle.downgrade(), error);
358                    RAW_IMAGE_DECODE_ERROR_EVENT.notify(args);
359                    UPDATES.once_next_update("", move || {
360                        let _hold_once = &handle;
361                    });
362                }
363            }
364            Event::ImageEncoded { task, data } => VIEW_PROCESS.on_image_encoded(task, data),
365            Event::ImageEncodeError { task, error } => {
366                VIEW_PROCESS.on_image_encode_error(task, error);
367            }
368
369            Event::AudioMetadataDecoded(meta) => {
370                if let Some(handle) = VIEW_PROCESS.on_audio_metadata(&meta) {
371                    let args = RawAudioMetadataDecodedArgs::now(handle.downgrade(), meta);
372                    RAW_AUDIO_METADATA_DECODED_EVENT.notify(args);
373                    UPDATES.once_next_update("", move || {
374                        let _hold_once = &handle;
375                    });
376                } else {
377                    tracing::warn!("received unknown audio metadata {:?}, ignoring", meta.id);
378                }
379            }
380            Event::AudioDecoded(audio) => {
381                if let Some(handle) = VIEW_PROCESS.on_audio_decoded(&audio) {
382                    let audio = ArcEq::new(audio);
383                    let args = RawAudioDecodedArgs::now(handle.downgrade(), ArcEq::downgrade(&audio));
384                    RAW_AUDIO_DECODED_EVENT.notify(args);
385                    UPDATES.once_next_update("", move || {
386                        let _hold_once = (&handle, &audio);
387                    });
388                } else {
389                    tracing::warn!("received unknown audio metadata {:?}, ignoring", audio.id);
390                }
391            }
392            Event::AudioDecodeError { audio: id, error } => {
393                if let Some(handle) = VIEW_PROCESS.on_audio_error(id) {
394                    let args = RawAudioDecodeErrorArgs::now(handle.downgrade(), error);
395                    RAW_AUDIO_DECODE_ERROR_EVENT.notify(args);
396                    UPDATES.once_next_update("", move || {
397                        let _hold_once = &handle;
398                    });
399                }
400            }
401
402            Event::AudioOutputOpened(id, data) => {
403                let a_id = audio_output_id(id);
404                let output = VIEW_PROCESS.on_audio_output_opened(a_id, data);
405
406                let args = RawAudioOutputOpenArgs::now(a_id, output.downgrade());
407                RAW_AUDIO_OUTPUT_OPEN_EVENT.notify(args);
408                UPDATES.once_next_update("", move || {
409                    let _hold_once = &output;
410                });
411            }
412            Event::AudioOutputOpenError { id, error } => {
413                let a_id = audio_output_id(id);
414
415                let args = RawAudioOutputOpenErrorArgs::now(a_id, error);
416                RAW_AUDIO_OUTPUT_OPEN_ERROR_EVENT.notify(args);
417            }
418
419            Event::AccessInit { window: w_id } => {
420                crate::access::on_access_init(window_id(w_id));
421            }
422            Event::AccessCommand {
423                window: win_id,
424                target: wgt_id,
425                command,
426            } => {
427                crate::access::on_access_command(window_id(win_id), WidgetId::from_raw(wgt_id.0), command);
428            }
429            Event::AccessDeinit { window: w_id } => {
430                crate::access::on_access_deinit(window_id(w_id));
431            }
432
433            // native dialog responses
434            Event::MsgDialogResponse(id, response) => {
435                VIEW_PROCESS.on_message_dlg_response(id, response);
436            }
437            Event::FileDialogResponse(id, response) => {
438                VIEW_PROCESS.on_file_dlg_response(id, response);
439            }
440            Event::NotificationResponse(id, response) => {
441                VIEW_PROCESS.on_notification_dlg_response(id, response);
442            }
443
444            Event::MenuCommand { id } => {
445                let _ = id;
446            }
447
448            // custom
449            Event::ExtensionEvent(id, payload) => {
450                let args = RawExtensionEventArgs::now(id, payload);
451                RAW_EXTENSION_EVENT.notify(args);
452            }
453
454            // config events
455            Event::FontsChanged => {
456                let args = RawFontChangedArgs::now();
457                RAW_FONT_CHANGED_EVENT.notify(args);
458            }
459            Event::FontAaChanged(aa) => {
460                let args = RawFontAaChangedArgs::now(aa);
461                RAW_FONT_AA_CHANGED_EVENT.notify(args);
462            }
463            Event::MultiClickConfigChanged(cfg) => {
464                let args = RawMultiClickConfigChangedArgs::now(cfg);
465                RAW_MULTI_CLICK_CONFIG_CHANGED_EVENT.notify(args);
466            }
467            Event::AnimationsConfigChanged(cfg) => {
468                VARS_APP.set_sys_animations_enabled(cfg.enabled);
469                let args = RawAnimationsConfigChangedArgs::now(cfg);
470                RAW_ANIMATIONS_CONFIG_CHANGED_EVENT.notify(args);
471            }
472            Event::KeyRepeatConfigChanged(cfg) => {
473                let args = RawKeyRepeatConfigChangedArgs::now(cfg);
474                RAW_KEY_REPEAT_CONFIG_CHANGED_EVENT.notify(args);
475            }
476            Event::TouchConfigChanged(cfg) => {
477                let args = RawTouchConfigChangedArgs::now(cfg);
478                RAW_TOUCH_CONFIG_CHANGED_EVENT.notify(args);
479            }
480            Event::LocaleChanged(cfg) => {
481                let args = RawLocaleChangedArgs::now(cfg);
482                RAW_LOCALE_CONFIG_CHANGED_EVENT.notify(args);
483            }
484            Event::ColorsConfigChanged(cfg) => {
485                let args = RawColorsConfigChangedArgs::now(cfg);
486                RAW_COLORS_CONFIG_CHANGED_EVENT.notify(args);
487            }
488
489            // `device_events`
490            Event::InputDevicesChanged(devices) => {
491                let devices: HashMap<_, _> = devices.into_iter().map(|(d_id, info)| (self.input_device_id(d_id), info)).collect();
492                INPUT_DEVICES.update(devices.clone());
493                let args = InputDevicesChangedArgs::now(devices);
494                INPUT_DEVICES_CHANGED_EVENT.notify(args);
495            }
496            Event::InputDeviceEvent { device, event } => {
497                let d_id = self.input_device_id(device);
498                match event {
499                    InputDeviceEvent::PointerMotion { delta } => {
500                        let args = PointerMotionArgs::now(d_id, delta);
501                        POINTER_MOTION_EVENT.notify(args);
502                    }
503                    InputDeviceEvent::ScrollMotion { delta } => {
504                        let args = ScrollMotionArgs::now(d_id, delta);
505                        SCROLL_MOTION_EVENT.notify(args);
506                    }
507                    InputDeviceEvent::AxisMotion { axis, value } => {
508                        let args = AxisMotionArgs::now(d_id, axis, value);
509                        AXIS_MOTION_EVENT.notify(args);
510                    }
511                    InputDeviceEvent::Button { button, state } => {
512                        let args = ButtonArgs::now(d_id, button, state);
513                        BUTTON_EVENT.notify(args);
514                    }
515                    InputDeviceEvent::Key { key_code, state } => {
516                        let args = KeyArgs::now(d_id, key_code, state);
517                        KEY_EVENT.notify(args);
518                    }
519                    _ => {}
520                }
521            }
522
523            Event::LowMemory => {
524                LOW_MEMORY_EVENT.notify(LowMemoryArgs::now());
525            }
526
527            Event::RecoveredFromComponentPanic { component, recover, panic } => {
528                tracing::error!(
529                    "view-process recovered from internal component panic\n  component: {component}\n  recover: {recover}\n```panic\n{panic}\n```"
530                );
531            }
532
533            // Others
534            Event::Inited(zng_view_api::ViewProcessInfo { .. }) | Event::Suspended | Event::Disconnected(_) | Event::FrameRendered(_) => {
535                unreachable!()
536            } // handled before coalesce.
537
538            _ => {}
539        }
540    }
541
542    /// Process a [`Event::FrameRendered`] event.
543    fn on_view_rendered_event(&mut self, ev: zng_view_api::window::EventFrameRendered) {
544        debug_assert!(ev.window != zng_view_api::window::WindowId::INVALID);
545        let window_id = WindowId::from_raw(ev.window.get());
546        // view.on_frame_rendered(window_id); // already called in push_coalesce
547        let image = ev.frame_image.map(|img| (VIEW_PROCESS.on_frame_image(&img), img)).map(ArcEq::new);
548        let args = crate::view_process::raw_events::RawFrameRenderedArgs::now(window_id, ev.frame, image.as_ref().map(ArcEq::downgrade));
549        RAW_FRAME_RENDERED_EVENT.notify(args);
550        if image.is_some() {
551            UPDATES.once_next_update("", move || {
552                let _hold_once = &image;
553            });
554        }
555    }
556
557    pub(crate) fn run_headed(mut self) {
558        self.apply_updates();
559        let mut wait = false;
560        loop {
561            wait = match self.poll(wait) {
562                AppControlFlow::Poll => false,
563                AppControlFlow::Wait => true,
564                AppControlFlow::Exit => break,
565            };
566        }
567    }
568
569    fn push_coalesce(&mut self, ev: AppEvent) {
570        match ev {
571            AppEvent::ViewEvent(ev) => match ev {
572                zng_view_api::Event::FrameRendered(ev) => {
573                    if ev.window == zng_view_api::window::WindowId::INVALID {
574                        tracing::error!("ignored rendered event for invalid window id, {ev:?}");
575                        return;
576                    }
577
578                    let window = WindowId::from_raw(ev.window.get());
579
580                    // update ViewProcess immediately, affects is_busy flag.
581                    {
582                        if VIEW_PROCESS.is_available() {
583                            VIEW_PROCESS.on_frame_rendered(window);
584                        }
585                    }
586
587                    self.pending_view_frame_events.push(ev);
588                }
589                zng_view_api::Event::Pong(count) => VIEW_PROCESS.on_pong(count),
590                zng_view_api::Event::Inited(inited) => {
591                    // notify immediately.
592                    if inited.is_respawn {
593                        VIEW_PROCESS.on_respawned(inited.generation);
594                        APP_PROCESS_SV.read().is_suspended.set(false);
595                    }
596
597                    VIEW_PROCESS.handle_inited(&inited);
598
599                    let args = crate::view_process::ViewProcessInitedArgs::now(inited);
600                    VIEW_PROCESS_INITED_EVENT.notify(args);
601                }
602                zng_view_api::Event::Suspended => {
603                    VIEW_PROCESS.handle_suspended();
604                    let args = crate::view_process::ViewProcessSuspendedArgs::now();
605                    VIEW_PROCESS_SUSPENDED_EVENT.notify(args);
606                    APP_PROCESS_SV.read().is_suspended.set(true);
607                }
608                zng_view_api::Event::Disconnected(vp_gen) => {
609                    // update ViewProcess immediately.
610                    VIEW_PROCESS.handle_disconnect(vp_gen);
611                }
612                ev => {
613                    if let Some(last) = self.pending_view_events.last_mut() {
614                        match last.coalesce(ev) {
615                            Ok(()) => {}
616                            Err(ev) => self.pending_view_events.push(ev),
617                        }
618                    } else {
619                        self.pending_view_events.push(ev);
620                    }
621                }
622            },
623            AppEvent::Update(op, target) => {
624                UPDATES.update_op(op, target);
625            }
626            AppEvent::UpdateApp => {
627                UPDATES.update_app();
628            }
629            AppEvent::ResumeUnwind(p) => std::panic::resume_unwind(p),
630        }
631    }
632
633    fn has_pending_updates(&mut self) -> bool {
634        !self.pending_view_events.is_empty() || self.pending.has_updates() || UPDATES.has_pending_updates() || !self.receiver.is_empty()
635    }
636
637    pub(crate) fn poll(&mut self, wait_app_event: bool) -> AppControlFlow {
638        let mut disconnected = false;
639
640        if self.exited {
641            return AppControlFlow::Exit;
642        }
643
644        if wait_app_event {
645            const PING_TIMER: Duration = Duration::from_secs(2);
646
647            let ping_timer = Deadline::timeout(PING_TIMER);
648            let timer = if self.view_process_is_busy() {
649                None
650            } else {
651                self.loop_timer.deadline().map(|t| t.min(ping_timer))
652            };
653            match self.receiver.recv_deadline_blocking(timer.unwrap_or(ping_timer)) {
654                Ok(ev) => {
655                    self.last_wait_event = Instant::now();
656                    self.push_coalesce(ev)
657                }
658                Err(e) => match e {
659                    ChannelError::Timeout => {
660                        if VIEW_PROCESS.is_available()
661                            && self.last_wait_event.elapsed() >= PING_TIMER
662                            && !VIEW_PROCESS.is_same_process()
663                            && VIEW_PROCESS.is_connected()
664                        {
665                            VIEW_PROCESS.ping();
666                        }
667                    }
668                    ChannelError::Disconnected { .. } => disconnected = true,
669                },
670            }
671        }
672        loop {
673            match self.receiver.try_recv() {
674                Ok(ev) => match ev {
675                    Some(ev) => self.push_coalesce(ev),
676                    None => break,
677                },
678                Err(e) => match e {
679                    ChannelError::Disconnected { .. } => {
680                        disconnected = true;
681                        break;
682                    }
683                    _ => unreachable!(),
684                },
685            }
686        }
687        if disconnected {
688            panic!("app events channel disconnected");
689        }
690
691        if self.view_process_is_busy() {
692            return AppControlFlow::Wait;
693        }
694
695        UPDATES.on_app_awake();
696
697        // clear timers.
698        let updated_timers = self.loop_timer.awake();
699        if updated_timers {
700            // tick timers and collect not elapsed timers.
701            UPDATES.update_timers(&mut self.loop_timer);
702            self.apply_updates();
703        }
704
705        let mut events = mem::take(&mut self.pending_view_events);
706        for ev in events.drain(..) {
707            self.on_view_event(ev);
708            self.apply_updates();
709        }
710        debug_assert!(self.pending_view_events.is_empty());
711        self.pending_view_events = events; // reuse capacity
712
713        let mut events = mem::take(&mut self.pending_view_frame_events);
714        for ev in events.drain(..) {
715            self.on_view_rendered_event(ev);
716        }
717        self.pending_view_frame_events = events;
718
719        if self.has_pending_updates() {
720            self.apply_updates();
721        }
722
723        self.finish_frame();
724
725        UPDATES.next_deadline(&mut self.loop_timer);
726
727        if APP_PROCESS_SV.read().exit {
728            UPDATES.on_app_sleep();
729            self.exited = true;
730            AppControlFlow::Exit
731        } else if self.has_pending_updates()
732            || UPDATES.has_pending_layout_or_render()
733            || matches!(self.loop_timer.deadline(), Some(t) if t.has_elapsed())
734        {
735            AppControlFlow::Poll
736        } else {
737            UPDATES.on_app_sleep();
738            AppControlFlow::Wait
739        }
740    }
741
742    /// Does updates, collects pending update generated events and layout + render.
743    fn apply_updates(&mut self) {
744        let _s = tracing::debug_span!("apply_updates").entered();
745
746        let mut run = true;
747        while run {
748            run = self.loop_monitor.update(|| {
749                let mut any = false;
750
751                self.pending |= UPDATES.apply_info();
752                if mem::take(&mut self.pending.info) {
753                    any = true;
754                    let _s = tracing::debug_span!("info").entered();
755
756                    let mut info_widgets = mem::take(&mut self.pending.info_widgets);
757
758                    let _t = INSTANT_APP.pause_for_update();
759
760                    WINDOWS_APP.update_info(&mut info_widgets);
761                }
762
763                {
764                    let _s = tracing::debug_span!("hooks").entered();
765                    self.pending |= UPDATES.apply_updates();
766                }
767
768                TimersService::notify();
769                if mem::take(&mut self.pending.update) {
770                    any = true;
771                    let _s = tracing::debug_span!("update").entered();
772
773                    let mut update_widgets = mem::take(&mut self.pending.update_widgets);
774
775                    let _t = INSTANT_APP.pause_for_update();
776
777                    UPDATES.on_pre_updates();
778
779                    WINDOWS_APP.update_widgets(&mut update_widgets);
780
781                    UPDATES.on_updates();
782                }
783
784                any
785            });
786        }
787    }
788
789    fn view_process_is_busy(&mut self) -> bool {
790        VIEW_PROCESS.is_available() && VIEW_PROCESS.is_busy()
791    }
792
793    // apply pending layout & render if the view-process is not already rendering.
794    fn finish_frame(&mut self) {
795        self.pending |= UPDATES.apply_layout_render();
796
797        while mem::take(&mut self.pending.layout) {
798            let _s = tracing::debug_span!("apply_layout").entered();
799
800            let mut layout_widgets = mem::take(&mut self.pending.layout_widgets);
801
802            self.loop_monitor.maybe_trace(|| {
803                let _t = INSTANT_APP.pause_for_update();
804
805                WINDOWS_APP.update_layout(&mut layout_widgets);
806            });
807
808            self.apply_updates();
809            self.pending |= UPDATES.apply_layout_render();
810        }
811
812        if mem::take(&mut self.pending.render) {
813            let _s = tracing::debug_span!("apply_render").entered();
814
815            let mut render_widgets = mem::take(&mut self.pending.render_widgets);
816            let mut render_update_widgets = mem::take(&mut self.pending.render_update_widgets);
817
818            let _t = INSTANT_APP.pause_for_update();
819
820            WINDOWS_APP.update_render(&mut render_widgets, &mut render_update_widgets);
821        }
822
823        self.loop_monitor.finish_frame();
824    }
825}
826
827/// Arguments for [`APP.on_init`] handlers.
828///
829/// No args as of this release. The handler is called in the new app context, so you can access any service inside.
830///
831/// [`APP.on_init`]: APP::on_init
832#[derive(Clone, Debug)]
833#[non_exhaustive]
834pub struct AppInitArgs {
835    /// If init handlers should load only required resources.
836    pub is_minimal: bool,
837}
838
839/// Arguments for [`APP.on_deinit`] handlers.
840///
841/// No args as of this release. The handler is called in the app context.
842///
843/// [`APP.on_deinit`]: APP::on_deinit
844#[derive(Clone, Debug)]
845#[non_exhaustive]
846pub struct AppDeinitArgs {}
847
848impl APP {
849    /// Register a handler to be called when the app starts.
850    ///
851    /// In single app builds (without `"multi_app"` feature) the `handler` is called only once and dropped.
852    ///
853    /// In `"multi_app"` builds the `handler` can be called more than once. The handler is called in the new app context, but
854    /// it lives in the app process lifetime, you can unsubscribe from the inside or just use `hn_once!` to drop on init.
855    ///
856    /// This method must be called before any other `APP` method, the `handler` is not called for an already running app.
857    ///
858    /// Async handlers are fully supported, the code before the first `.await` runs blocking the rest runs in the `UPDATES` service.
859    pub fn on_init(&self, handler: crate::handler::Handler<AppInitArgs>) {
860        zng_unique_id::hot_static_ref!(ON_INIT).lock().push(handler);
861    }
862
863    /// Register a handler to be called when the app exits.
864    ///
865    /// The `handler` is called only once, it runs in the app context and is dropped, any async tasks or requests that require app updates will
866    /// not work, the app will exit just after calling the handler.
867    ///
868    /// This method must be called in the app context.
869    pub fn on_deinit(&self, handler: impl FnOnce(&AppDeinitArgs) + Send + 'static) {
870        ON_DEINIT.write().get_mut().push(Box::new(handler));
871    }
872
873    fn call_init_handlers(&self, is_minimal: bool) {
874        #[cfg(feature = "multi_app")]
875        let _lock = zng_unique_id::hot_static_ref!(ON_INIT_CALL).lock();
876
877        let mut handlers = mem::take(&mut *zng_unique_id::hot_static_ref!(ON_INIT).lock());
878        let args = AppInitArgs { is_minimal };
879        handlers.retain_mut(|h| {
880            let (owner, handle) = zng_handle::Handle::new(());
881            h.app_event(Box::new(handle.downgrade()), true, &args);
882            !owner.is_dropped()
883        });
884
885        let mut s = zng_unique_id::hot_static_ref!(ON_INIT).lock();
886        handlers.extend(s.drain(..));
887        *s = handlers;
888    }
889
890    fn call_deinit_handlers(&self) {
891        let handlers = mem::take(&mut *ON_DEINIT.write().get_mut());
892        let args = AppDeinitArgs {};
893        for h in handlers {
894            h(&args);
895        }
896    }
897}
898zng_unique_id::hot_static! {
899    static ON_INIT: Mutex<Vec<crate::handler::Handler<AppInitArgs>>> = Mutex::new(vec![]);
900}
901#[cfg(feature = "multi_app")]
902zng_unique_id::hot_static! {
903    static ON_INIT_CALL: Mutex<()> = Mutex::new(());
904}
905app_local! {
906    // Mutex for Sync only
907    static ON_DEINIT: Mutex<Vec<Box<dyn FnOnce(&AppDeinitArgs) + Send + 'static>>> = const { Mutex::new(vec![]) };
908}
909
910/// App main loop timer.
911#[derive(Debug)]
912pub(crate) struct LoopTimer {
913    now: DInstant,
914    deadline: Option<Deadline>,
915}
916impl Default for LoopTimer {
917    fn default() -> Self {
918        Self {
919            now: INSTANT.now(),
920            deadline: None,
921        }
922    }
923}
924impl LoopTimer {
925    /// Returns `true` if the `deadline` has elapsed, `false` if the `deadline` was
926    /// registered for future waking.
927    pub fn elapsed(&mut self, deadline: Deadline) -> bool {
928        if deadline.0 <= self.now {
929            true
930        } else {
931            self.register(deadline);
932            false
933        }
934    }
935
936    /// Register the future `deadline`.
937    pub fn register(&mut self, deadline: Deadline) {
938        if let Some(d) = &mut self.deadline {
939            if deadline < *d {
940                *d = deadline;
941            }
942        } else {
943            self.deadline = Some(deadline)
944        }
945    }
946
947    /// Get next recv deadline.
948    pub(crate) fn deadline(&self) -> Option<Deadline> {
949        self.deadline
950    }
951
952    /// Maybe awake timer.
953    pub(crate) fn awake(&mut self) -> bool {
954        self.now = INSTANT.now();
955        if let Some(d) = self.deadline
956            && d.0 <= self.now
957        {
958            self.deadline = None;
959            return true;
960        }
961        false
962    }
963
964    /// Awake timestamp.
965    pub fn now(&self) -> DInstant {
966        self.now
967    }
968}
969impl zng_var::animation::AnimationTimer for LoopTimer {
970    fn elapsed(&mut self, deadline: Deadline) -> bool {
971        self.elapsed(deadline)
972    }
973
974    fn register(&mut self, deadline: Deadline) {
975        self.register(deadline)
976    }
977
978    fn now(&self) -> DInstant {
979        self.now()
980    }
981}
982
983#[derive(Default)]
984struct LoopMonitor {
985    update_count: u16,
986    skipped: bool,
987    trace: Vec<UpdateTrace>,
988}
989impl LoopMonitor {
990    /// Returns `false` if the loop should break.
991    pub fn update(&mut self, update_once: impl FnOnce() -> bool) -> bool {
992        self.update_count += 1;
993
994        if self.update_count < 500 {
995            update_once()
996        } else if self.update_count < 1000 {
997            UpdatesTrace::collect_trace(&mut self.trace, update_once)
998        } else if self.update_count == 1000 {
999            self.skipped = true;
1000            let trace = UpdatesTrace::format_trace(mem::take(&mut self.trace));
1001            tracing::error!(
1002                "updated 1000 times without rendering, probably stuck in an infinite loop\n\
1003                 will start skipping updates to render and poll system events\n\
1004                 top 20 most frequent update requests (in 500 cycles):\n\
1005                 {trace}\n\
1006                    you can use `UpdatesTraceUiNodeExt` and `updates_trace_event` to refine the trace"
1007            );
1008            false
1009        } else if self.update_count == 1500 {
1010            self.update_count = 1001;
1011            false
1012        } else {
1013            update_once()
1014        }
1015    }
1016
1017    pub fn maybe_trace(&mut self, notify_once: impl FnOnce()) {
1018        if (500..1000).contains(&self.update_count) {
1019            UpdatesTrace::collect_trace(&mut self.trace, notify_once);
1020        } else {
1021            notify_once();
1022        }
1023    }
1024
1025    pub fn finish_frame(&mut self) {
1026        if !self.skipped {
1027            self.skipped = false;
1028            self.update_count = 0;
1029            self.trace = vec![];
1030        }
1031    }
1032}
1033
1034impl APP {
1035    /// Pre-init intrinsic services and commands, must be called before extensions init.
1036    pub(super) fn pre_init(&self, is_headed: bool, with_renderer: bool, view_process_exe: PathBuf, view_process_env: HashMap<Txt, Txt>) {
1037        // apply `pause_time_for_updates`
1038        let s = APP_PROCESS_SV.read();
1039        s.pause_time_for_updates
1040            .hook(|a| {
1041                if !matches!(INSTANT.mode(), zng_time::InstantMode::Manual) {
1042                    if *a.value() {
1043                        INSTANT_APP.set_mode(InstantMode::UpdatePaused);
1044                    } else {
1045                        INSTANT_APP.set_mode(InstantMode::Now);
1046                    }
1047                }
1048                true
1049            })
1050            .perm();
1051
1052        // (re)apply `device_events_filter` on process init.
1053        VIEW_PROCESS_INITED_EVENT
1054            .hook(|_| {
1055                let filter = APP_PROCESS_SV.read().device_events_filter.get();
1056                if !filter.is_empty()
1057                    && let Err(e) = VIEW_PROCESS.set_device_events_filter(filter)
1058                {
1059                    tracing::error!("cannot set device events on the view-process, {e}");
1060                }
1061                true
1062            })
1063            .perm();
1064
1065        // implement `EXIT_CMD`, let any other handler intercept it first
1066        EXIT_CMD
1067            .on_event(
1068                true,
1069                true,
1070                false,
1071                crate::hn!(|a| {
1072                    a.propagation.stop();
1073                    APP.exit();
1074                }),
1075            )
1076            .perm();
1077
1078        // apply `device_events_filter`
1079        s.device_events_filter
1080            .hook(|a| {
1081                if let Err(e) = VIEW_PROCESS.set_device_events_filter(a.value().clone()) {
1082                    tracing::error!("cannot set device events on the view-process, {e}");
1083                }
1084                true
1085            })
1086            .perm();
1087
1088        // spawn view-process
1089        if is_headed {
1090            debug_assert!(with_renderer);
1091
1092            let view_evs_sender = UPDATES.sender();
1093            VIEW_PROCESS.start(view_process_exe, view_process_env, false, move |ev| {
1094                let _ = view_evs_sender.send_view_event(ev);
1095            });
1096        } else if with_renderer {
1097            let view_evs_sender = UPDATES.sender();
1098            VIEW_PROCESS.start(view_process_exe, view_process_env, true, move |ev| {
1099                let _ = view_evs_sender.send_view_event(ev);
1100            });
1101        }
1102    }
1103}
1104
1105impl APP {
1106    /// Register a request for process exit with code `0` in the next update.
1107    ///
1108    /// The [`EXIT_REQUESTED_EVENT`] will notify, and if propagation is not cancelled the app process will exit.
1109    ///
1110    /// Returns a response variable that is updated once with the unit value [`ExitCancelled`]
1111    /// if the exit operation is cancelled.
1112    ///
1113    /// See also the [`EXIT_CMD`].
1114    pub fn exit(&self) -> ResponseVar<ExitCancelled> {
1115        let mut s = APP_PROCESS_SV.write();
1116        if let Some(r) = &s.exit_requests {
1117            r.response_var()
1118        } else {
1119            let (responder, response) = response_var();
1120            s.exit_requests = Some(responder);
1121            EXIT_REQUESTED_EVENT.notify(ExitRequestedArgs::now());
1122            EXIT_REQUESTED_EVENT
1123                .on_event(
1124                    true,
1125                    crate::hn_once!(|args: &ExitRequestedArgs| {
1126                        let mut s = APP_PROCESS_SV.write();
1127                        if !args.propagation.is_stopped() {
1128                            s.exit = true;
1129                        } else {
1130                            s.exit_requests.take().unwrap().respond(ExitCancelled);
1131                        }
1132                    }),
1133                )
1134                .perm();
1135            response
1136        }
1137    }
1138
1139    /// Gets a variable that tracks if the app is suspended by the operating system.
1140    ///
1141    /// Suspended apps cannot create graphics contexts and are likely to be killed if the user does not
1142    /// return. Operations that persist data should flush on suspension.
1143    ///
1144    /// App suspension is controlled by the view-process, the [`VIEW_PROCESS_SUSPENDED_EVENT`] notifies
1145    /// on suspension and the [`VIEW_PROCESS_INITED_EVENT`] notifies a "respawn" on resume.
1146    pub fn is_suspended(&self) -> Var<bool> {
1147        expr_var! {
1148            let inited = #{VIEW_PROCESS_INITED_EVENT.var_latest()};
1149            let sus = #{VIEW_PROCESS_SUSPENDED_EVENT.var_latest()};
1150
1151            match (sus, inited) {
1152                (_, None) => true,                               // never inited
1153                (None, Some(_)) => false,                        // inited, never suspended
1154                (Some(s), Some(i)) => s.timestamp > i.timestamp, // if suspended after last init
1155            }
1156        }
1157    }
1158}
1159
1160/// App time control.
1161///
1162/// The manual time methods are only recommended for headless apps. These methods apply immediately, there are not like service methods that
1163/// only apply after current update.
1164impl APP {
1165    /// Gets a variable that configures if [`INSTANT.now`] is the same exact value during each update, info, layout or render pass.
1166    ///
1167    /// Time is paused by default, setting this to `false` will cause [`INSTANT.now`] to read the system time for every call.
1168    ///
1169    /// [`INSTANT.now`]: crate::INSTANT::now
1170    pub fn pause_time_for_update(&self) -> Var<bool> {
1171        APP_PROCESS_SV.read().pause_time_for_updates.clone()
1172    }
1173
1174    /// Pause the [`INSTANT.now`] value, after this call it must be updated manually using
1175    /// [`advance_manual_time`] or [`set_manual_time`]. To resume normal time use [`end_manual_time`].
1176    ///
1177    /// [`INSTANT.now`]: crate::INSTANT::now
1178    /// [`advance_manual_time`]: Self::advance_manual_time
1179    /// [`set_manual_time`]: Self::set_manual_time
1180    /// [`end_manual_time`]: Self::end_manual_time
1181    pub fn start_manual_time(&self) {
1182        INSTANT_APP.set_mode(InstantMode::Manual);
1183        INSTANT_APP.set_now(INSTANT.now());
1184        UPDATES.update_app();
1185    }
1186
1187    /// Adds the `advance` to the current manual time.
1188    ///
1189    /// Note that you must ensure an update reaches the code that controls manual time, otherwise
1190    /// the app loop may end-up stuck on idle or awaiting a timer that never elapses.
1191    ///
1192    /// # Panics
1193    ///
1194    /// Panics if called before [`start_manual_time`].
1195    ///
1196    /// [`start_manual_time`]: Self::start_manual_time
1197    pub fn advance_manual_time(&self, advance: Duration) {
1198        INSTANT_APP.advance_now(advance);
1199        UPDATES.update_app();
1200    }
1201
1202    /// Set the current [`INSTANT.now`].
1203    ///
1204    /// # Panics
1205    ///
1206    /// Panics if called before [`start_manual_time`].
1207    ///
1208    /// [`INSTANT.now`]: crate::INSTANT::now
1209    /// [`start_manual_time`]: Self::start_manual_time
1210    pub fn set_manual_time(&self, now: DInstant) {
1211        INSTANT_APP.set_now(now);
1212        UPDATES.update_app();
1213    }
1214
1215    /// Resumes normal time.
1216    pub fn end_manual_time(&self) {
1217        INSTANT_APP.set_mode(match APP.pause_time_for_update().get() {
1218            true => InstantMode::UpdatePaused,
1219            false => InstantMode::Now,
1220        });
1221        UPDATES.update_app();
1222    }
1223}
1224
1225command! {
1226    /// Represents the app process [`exit`] request.
1227    ///
1228    /// [`exit`]: APP::exit
1229    pub static EXIT_CMD {
1230        l10n!: true,
1231        name: "Exit",
1232        info: "Close all windows and exit",
1233        shortcut: shortcut!(Exit),
1234    };
1235}
1236
1237/// Cancellation message of an [exit request].
1238///
1239/// [exit request]: APP::exit
1240#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1241pub struct ExitCancelled;
1242impl fmt::Display for ExitCancelled {
1243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1244        write!(f, "exit request cancelled")
1245    }
1246}
1247
1248pub(crate) fn assert_not_view_process() {
1249    if zng_view_api::ViewConfig::from_env().is_some() {
1250        panic!("cannot start App in view-process");
1251    }
1252}
1253/// When compiled with `"deadlock_detection"` spawns a thread that monitors for `parking_lot` deadlocks.
1254///
1255/// Note that this method is already called on app scope spawn.
1256/// You can call it before `zng::env::init!` to detect deadlocks in other processes too.
1257#[cfg(feature = "deadlock_detection")]
1258pub fn spawn_deadlock_detection() {
1259    use parking_lot::deadlock;
1260    use std::{
1261        sync::atomic::{self, AtomicBool},
1262        thread,
1263        time::*,
1264    };
1265
1266    static CHECK_RUNNING: AtomicBool = AtomicBool::new(false);
1267
1268    if CHECK_RUNNING.swap(true, atomic::Ordering::SeqCst) {
1269        return;
1270    }
1271
1272    thread::Builder::new()
1273        .name("deadlock_detection".into())
1274        .stack_size(256 * 1024)
1275        .spawn(|| {
1276            loop {
1277                thread::sleep(Duration::from_secs(10));
1278
1279                let deadlocks = deadlock::check_deadlock();
1280                if deadlocks.is_empty() {
1281                    continue;
1282                }
1283
1284                use std::fmt::Write;
1285                let mut msg = String::new();
1286
1287                let _ = writeln!(&mut msg, "{} deadlocks detected", deadlocks.len());
1288                for (i, threads) in deadlocks.iter().enumerate() {
1289                    let _ = writeln!(&mut msg, "Deadlock #{}, {} threads", i, threads.len());
1290                    for t in threads {
1291                        let _ = writeln!(&mut msg, "Thread Id {:#?}", t.thread_id());
1292                        let _ = writeln!(&mut msg, "{:#?}", t.backtrace());
1293                    }
1294                }
1295
1296                #[cfg(not(feature = "test_util"))]
1297                eprint!("{msg}");
1298
1299                #[cfg(feature = "test_util")]
1300                {
1301                    // test runner captures output and ignores panics in background threads, so
1302                    // we write directly to stderr and exit the process.
1303                    use std::io::Write;
1304                    let _ = write!(&mut std::io::stderr(), "{msg}");
1305                    zng_env::exit(-1);
1306                }
1307            }
1308        })
1309        .expect("failed to spawn thread");
1310}
1311/// When compiled with `"deadlock_detection"` spawns a thread that monitors for `parking_lot` deadlocks.
1312///
1313/// Note that this method is already called on app scope spawn.
1314/// You can call it before `zng::env::init!` to detect deadlocks in other processes too.
1315#[cfg(not(feature = "deadlock_detection"))]
1316pub fn spawn_deadlock_detection() {}
1317
1318app_local! {
1319    pub(super) static APP_PROCESS_SV: AppProcessService = AppProcessService {
1320        exit_requests: None,
1321        exit: false,
1322        device_events_filter: zng_var::var(Default::default()),
1323        pause_time_for_updates: zng_var::var(true),
1324        is_suspended: zng_var::var(false),
1325    };
1326}
1327
1328pub(super) struct AppProcessService {
1329    exit_requests: Option<ResponderVar<ExitCancelled>>,
1330    pub(crate) exit: bool,
1331    pub(crate) device_events_filter: Var<DeviceEventsFilter>,
1332    pause_time_for_updates: Var<bool>,
1333    is_suspended: Var<bool>,
1334}
1335
1336/// App events.
1337#[derive(Debug)]
1338#[allow(clippy::large_enum_variant)] // Event is the most used variant
1339pub(crate) enum AppEvent {
1340    /// Event from the View Process.
1341    ViewEvent(zng_view_api::Event),
1342    /// Do an update cycle.
1343    Update(UpdateOp, WidgetId),
1344    /// Resume a panic in the app main thread.
1345    ResumeUnwind(PanicPayload),
1346    /// Check for pending updates.
1347    UpdateApp,
1348}
1349
1350/// A sender that can awake apps and insert events into the main loop.
1351///
1352/// A Clone of the sender is available in [`UPDATES.sender`].
1353///
1354/// [`UPDATES.sender`]: crate::update::UPDATES::sender
1355#[derive(Clone)]
1356pub struct AppEventSender(channel::Sender<AppEvent>);
1357impl AppEventSender {
1358    pub(crate) fn new() -> (Self, channel::Receiver<AppEvent>) {
1359        let (sender, receiver) = channel::unbounded();
1360        (Self(sender), receiver)
1361    }
1362
1363    #[allow(clippy::result_large_err)] // error does not move far up the stack
1364    fn send_app_event(&self, event: AppEvent) -> Result<(), ChannelError> {
1365        self.0.send_blocking(event)
1366    }
1367
1368    #[allow(clippy::result_large_err)]
1369    fn send_view_event(&self, event: zng_view_api::Event) -> Result<(), ChannelError> {
1370        self.0.send_blocking(AppEvent::ViewEvent(event))
1371    }
1372
1373    /// Causes an update cycle to happen in the app, includes the `target` widget in the UI.
1374    pub fn send_update(&self, op: UpdateOp, target: WidgetId) -> Result<(), ChannelError> {
1375        UpdatesTrace::log_update();
1376        self.send_app_event(AppEvent::Update(op, target))
1377    }
1378
1379    /// Causes an update cycle to happens in the app.
1380    pub fn send_update_app(&self) -> Result<(), ChannelError> {
1381        UpdatesTrace::log_update();
1382        self.send_app_event(AppEvent::UpdateApp)
1383    }
1384
1385    /// Resume a panic in the app main loop thread.
1386    pub fn send_resume_unwind(&self, payload: PanicPayload) -> Result<(), ChannelError> {
1387        self.send_app_event(AppEvent::ResumeUnwind(payload))
1388    }
1389
1390    /// Create an [`Waker`] that causes a [`send_update`](Self::send_update).
1391    pub fn waker(&self, also_update: Option<WidgetId>) -> Waker {
1392        Arc::new(AppWaker(self.0.clone(), also_update)).into()
1393    }
1394}
1395
1396struct AppWaker(channel::Sender<AppEvent>, Option<WidgetId>);
1397impl std::task::Wake for AppWaker {
1398    fn wake(self: std::sync::Arc<Self>) {
1399        self.wake_by_ref()
1400    }
1401    fn wake_by_ref(self: &Arc<Self>) {
1402        match self.1 {
1403            Some(id) => {
1404                let _ = self.0.send_blocking(AppEvent::Update(UpdateOp::Update, id));
1405            }
1406            None => {
1407                let _ = self.0.send_blocking(AppEvent::UpdateApp);
1408            }
1409        }
1410    }
1411}
1412
1413type PanicPayload = Box<dyn std::any::Any + Send + 'static>;
1414
1415event_args! {
1416    /// Arguments for [`EXIT_REQUESTED_EVENT`].
1417    ///
1418    /// Requesting `propagation().stop()` on this event cancels the exit.
1419    pub struct ExitRequestedArgs {
1420
1421        ..
1422
1423        /// Broadcast to all.
1424        fn is_in_target(&self, _id: WidgetId) -> bool {
1425            true
1426        }
1427    }
1428}
1429
1430event! {
1431    /// Cancellable event raised when app process exit is requested.
1432    ///
1433    /// App exit can be requested using the [`APP`] service or the [`EXIT_CMD`], some extensions
1434    /// also request exit if some conditions are met, for example, `WindowManager` requests it after the last window
1435    /// is closed.
1436    ///
1437    /// Requesting `propagation().stop()` on this event cancels the exit.
1438    pub static EXIT_REQUESTED_EVENT: ExitRequestedArgs;
1439}