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