Skip to main content

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