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
33pub(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 _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 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 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 Event::ExtensionEvent(id, payload) => {
439 let args = RawExtensionEventArgs::now(id, payload);
440 RAW_EXTENSION_EVENT.notify(args);
441 }
442
443 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 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 Event::Inited(zng_view_api::ViewProcessInfo { .. }) | Event::Suspended | Event::Disconnected(_) | Event::FrameRendered(_) => {
524 unreachable!()
525 } _ => {}
528 }
529 }
530
531 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 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 = ℑ
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 {
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 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 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 let updated_timers = self.loop_timer.awake();
688 if updated_timers {
689 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; 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 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 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#[derive(Clone, Debug)]
822#[non_exhaustive]
823pub struct AppInitArgs {
824 pub is_minimal: bool,
826}
827
828#[derive(Clone, Debug)]
834#[non_exhaustive]
835pub struct AppDeinitArgs {}
836
837impl APP {
838 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 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 static ON_DEINIT: Mutex<Vec<Box<dyn FnOnce(&AppDeinitArgs) + Send + 'static>>> = const { Mutex::new(vec![]) };
897}
898
899#[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 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 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 pub(crate) fn deadline(&self) -> Option<Deadline> {
938 self.deadline
939 }
940
941 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 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 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 pub(super) fn pre_init(&self, is_headed: bool, with_renderer: bool, view_process_exe: PathBuf, view_process_env: HashMap<Txt, Txt>) {
1026 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 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 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 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 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 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 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, (None, Some(_)) => false, (Some(s), Some(i)) => s.timestamp > i.timestamp, }
1145 }
1146 }
1147}
1148
1149impl APP {
1154 pub fn pause_time_for_update(&self) -> Var<bool> {
1160 APP_PROCESS_SV.read().pause_time_for_updates.clone()
1161 }
1162
1163 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 pub fn advance_manual_time(&self, advance: Duration) {
1187 INSTANT_APP.advance_now(advance);
1188 UPDATES.update_app();
1189 }
1190
1191 pub fn set_manual_time(&self, now: DInstant) {
1200 INSTANT_APP.set_now(now);
1201 UPDATES.update_app();
1202 }
1203
1204 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 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#[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#[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 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#[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#[derive(Debug)]
1326#[allow(clippy::large_enum_variant)] pub(crate) enum AppEvent {
1328 ViewEvent(zng_view_api::Event),
1330 Update(UpdateOp, WidgetId),
1332 ResumeUnwind(PanicPayload),
1334 UpdateApp,
1336}
1337
1338#[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)] 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 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 pub fn send_update_app(&self) -> Result<(), ChannelError> {
1369 UpdatesTrace::log_update();
1370 self.send_app_event(AppEvent::UpdateApp)
1371 }
1372
1373 pub fn send_resume_unwind(&self, payload: PanicPayload) -> Result<(), ChannelError> {
1375 self.send_app_event(AppEvent::ResumeUnwind(payload))
1376 }
1377
1378 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 pub struct ExitRequestedArgs {
1408
1409 ..
1410
1411 fn is_in_target(&self, _id: WidgetId) -> bool {
1413 true
1414 }
1415 }
1416}
1417
1418event! {
1419 pub static EXIT_REQUESTED_EVENT: ExitRequestedArgs;
1427}