1#![allow(unused_variables)]
2
3use tao::{
4 event::{Event as TaoEvent, StartCause},
5 event_loop::{
6 ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy as TaoEventLoopProxy,
7 EventLoopWindowTarget as TaoEventLoopWindowTarget,
8 },
9 platform::run_return::EventLoopExtRunReturn,
10};
11use tauri_runtime::{
12 DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Result, RunEvent, Runtime,
13 RuntimeHandle, RuntimeInitArgs, UserEvent, WindowEventId,
14 dpi::PhysicalPosition,
15 monitor::Monitor,
16 webview::{DetachedWebview, PendingWebview},
17 window::{
18 DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WindowBuilder,
19 WindowEvent, WindowId,
20 },
21};
22use tauri_utils::Theme;
23use url::Url;
24use verso::CustomProtocolBuilder;
25
26use std::{
27 borrow::Cow,
28 collections::HashMap,
29 fmt::{self, Debug},
30 ops::Deref,
31 sync::{
32 Arc, Mutex,
33 atomic::{AtomicU32, Ordering},
34 mpsc::channel,
35 },
36 thread::{ThreadId, current as current_thread},
37};
38
39use crate::{
40 event_loop_ext::TaoEventLoopWindowTargetExt,
41 get_verso_path,
42 utils::{to_tao_theme, to_verso_theme},
43 webview::VersoWebviewDispatcher,
44 window::{VersoWindowDispatcher, Window},
45};
46
47type Task = Box<dyn FnOnce() + Send + 'static>;
48type TaskWithEventLoop<T> = Box<dyn FnOnce(&TaoEventLoopWindowTarget<Message<T>>) + Send + 'static>;
49
50pub enum Message<T: UserEvent> {
51 Task(Task),
52 TaskWithEventLoop(TaskWithEventLoop<T>),
54 CloseWindow(WindowId),
55 DestroyWindow(WindowId),
56 RequestExit(i32),
57 UserEvent(T),
58}
59
60impl<T: UserEvent> Clone for Message<T> {
61 fn clone(&self) -> Self {
62 match self {
63 Self::UserEvent(t) => Self::UserEvent(t.clone()),
64 _ => unimplemented!(),
65 }
66 }
67}
68
69#[derive(Clone)]
70pub struct DispatcherMainThreadContext<T: UserEvent> {
71 window_target: TaoEventLoopWindowTarget<Message<T>>,
72}
73
74#[allow(clippy::non_send_fields_in_send_ty)]
76unsafe impl<T: UserEvent> Send for DispatcherMainThreadContext<T> {}
77
78#[allow(clippy::non_send_fields_in_send_ty)]
80unsafe impl<T: UserEvent> Sync for DispatcherMainThreadContext<T> {}
81
82#[derive(Clone)]
83pub struct RuntimeContext<T: UserEvent> {
84 windows: Arc<Mutex<HashMap<WindowId, Window>>>,
85 prefered_theme: Arc<Mutex<Option<Theme>>>,
86 event_proxy: TaoEventLoopProxy<Message<T>>,
87 main_thread: DispatcherMainThreadContext<T>,
89 main_thread_id: ThreadId,
90 next_window_id: Arc<AtomicU32>,
91 next_webview_id: Arc<AtomicU32>,
92 next_window_event_id: Arc<AtomicU32>,
93 next_webview_event_id: Arc<AtomicU32>,
94}
95
96impl<T: UserEvent> RuntimeContext<T> {
97 pub fn send_message(&self, message: Message<T>) -> Result<()> {
98 if current_thread().id() == self.main_thread_id {
99 match message {
100 Message::Task(task) => {
101 task();
102 return Ok(());
103 }
104 Message::TaskWithEventLoop(task) => {
105 task(&self.main_thread.window_target);
106 return Ok(());
107 }
108 _ => {}
109 }
110 }
111 self.event_proxy
112 .send_event(message)
113 .map_err(|_| Error::FailedToSendMessage)?;
114 Ok(())
115 }
116
117 pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
119 self.send_message(Message::Task(Box::new(f)))
120 }
121
122 pub fn run_on_main_thread_with_event_loop<
124 X: Send + Sync + 'static,
125 F: FnOnce(&TaoEventLoopWindowTarget<Message<T>>) -> X + Send + 'static,
126 >(
127 &self,
128 f: F,
129 ) -> Result<X> {
130 let (tx, rx) = channel();
131 self.send_message(Message::TaskWithEventLoop(Box::new(move |e| {
132 let _ = tx.send(f(e));
133 })))?;
134 rx.recv()
135 .map_err(|_| tauri_runtime::Error::FailedToReceiveMessage)
136 }
137
138 pub fn next_window_id(&self) -> WindowId {
139 self.next_window_id.fetch_add(1, Ordering::Relaxed).into()
140 }
141
142 pub fn next_webview_id(&self) -> u32 {
143 self.next_webview_id.fetch_add(1, Ordering::Relaxed)
144 }
145
146 pub fn next_window_event_id(&self) -> WindowEventId {
147 self.next_window_event_id.fetch_add(1, Ordering::Relaxed)
148 }
149
150 pub fn next_webview_event_id(&self) -> WindowEventId {
151 self.next_webview_event_id.fetch_add(1, Ordering::Relaxed)
152 }
153
154 pub fn create_window<
159 R: Runtime<
160 T,
161 WindowDispatcher = VersoWindowDispatcher<T>,
162 WebviewDispatcher = VersoWebviewDispatcher<T>,
163 >,
164 F: Fn(RawWindow<'_>) + Send + 'static,
165 >(
166 &self,
167 pending: PendingWindow<T, R>,
168 _after_window_creation: Option<F>,
169 ) -> Result<DetachedWindow<T, R>> {
170 let label = pending.label;
171 let Some(pending_webview) = pending.webview else {
172 return Err(tauri_runtime::Error::CreateWindow);
173 };
174
175 let window_id = self.next_window_id();
176 let webview_id = self.next_webview_id();
177
178 let mut window_builder = pending.window_builder;
179
180 if window_builder.get_theme().is_none() {
181 window_builder = window_builder.theme(*self.prefered_theme.lock().unwrap());
182 }
183
184 let webview = window_builder
185 .verso_builder
186 .user_scripts(
187 pending_webview
188 .webview_attributes
189 .initialization_scripts
190 .into_iter()
191 .map(|script| script.script),
192 )
193 .custom_protocols(
194 pending_webview
195 .uri_scheme_protocols
196 .keys()
197 .map(CustomProtocolBuilder::new),
198 )
199 .build(get_verso_path(), Url::parse(&pending_webview.url).unwrap());
200
201 let webview_label = label.clone();
202 let sender = self.event_proxy.clone();
203 let uri_scheme_protocols: HashMap<_, _> = pending_webview
204 .uri_scheme_protocols
205 .into_iter()
206 .map(|(key, value)| (key, Arc::new(value)))
207 .collect();
208 webview
209 .on_web_resource_requested(move |mut request, response_fn| {
210 if !request.headers().contains_key("Origin") {
214 #[cfg(windows)]
215 let uri = {
216 let scheme = if pending_webview.webview_attributes.use_https_scheme {
217 "https"
218 } else {
219 "http"
220 };
221 format!("{scheme}://tauri.localhost")
222 };
223 #[cfg(not(windows))]
224 let uri = "tauri://localhost";
225 request.headers_mut().insert("Origin", uri.parse().unwrap());
226 }
227 for (scheme, handler) in &uri_scheme_protocols {
228 if scheme == "ipc" {
231 if let Some(data) = request
232 .headers_mut()
233 .remove("Tauri-VersoRuntime-Invoke-Body")
234 {
235 if let Ok(body) =
236 percent_encoding::percent_decode(data.as_bytes()).decode_utf8()
237 {
238 *request.body_mut() = body.as_bytes().to_vec();
239 } else {
240 log::error!("IPC invoke body header is not a valid UTF-8 string");
241 }
242 }
243 }
244 #[cfg(windows)]
245 let (uri, http_or_https) = (
246 request.uri().to_string(),
247 if pending_webview.webview_attributes.use_https_scheme {
248 "https"
249 } else {
250 "http"
251 },
252 );
253 #[cfg(windows)]
254 let is_custom_protocol_uri = is_work_around_uri(&uri, http_or_https, scheme);
255 #[cfg(not(windows))]
256 let is_custom_protocol_uri = request.uri().scheme_str() == Some(scheme);
257 if is_custom_protocol_uri {
258 #[cfg(windows)]
259 {
260 if let Ok(reverted) =
261 revert_custom_protocol_work_around(&uri, http_or_https, scheme)
262 {
263 *request.uri_mut() = reverted
264 } else {
265 log::error!("Can't revert the URI work around on: {uri}")
266 };
267 }
268 let handler = handler.clone();
270 let webview_label = webview_label.clone();
271 let _ = sender.send_event(Message::Task(Box::new(move || {
272 handler(
273 &webview_label,
274 request,
275 Box::new(move |response| {
276 response_fn(Some(response.map(Cow::into_owned)));
277 }),
278 );
279 })));
280 return;
281 }
282 }
283 response_fn(None);
284 })
285 .map_err(|_| tauri_runtime::Error::CreateWindow)?;
286
287 if let Some(navigation_handler) = pending_webview.navigation_handler {
288 if let Err(error) = webview.on_navigation_starting(move |url| navigation_handler(&url))
289 {
290 log::error!(
291 "Register `on_navigation_starting` failed with {error}, `navigation_handler` will not get called for this window ({label})!"
292 );
293 }
294 }
295
296 let sender = self.event_proxy.clone();
297 webview
298 .on_close_requested(move || {
299 let _ = sender.send_event(Message::CloseWindow(window_id));
300 })
301 .map_err(|_| tauri_runtime::Error::CreateWindow)?;
302
303 let on_window_event_listeners = Arc::new(Mutex::new(HashMap::new()));
304
305 let webview = Arc::new(Mutex::new(webview));
306 let window = Window {
307 label: label.clone(),
308 webview: webview.clone(),
309 on_window_event_listeners: on_window_event_listeners.clone(),
310 };
311
312 self.windows.lock().unwrap().insert(window_id, window);
313
314 Ok(DetachedWindow {
315 id: window_id,
316 label: label.clone(),
317 dispatcher: VersoWindowDispatcher {
318 id: window_id,
319 context: self.clone(),
320 webview: webview.clone(),
321 on_window_event_listeners,
322 },
323 webview: Some(DetachedWindowWebview {
324 webview: DetachedWebview {
325 label,
326 dispatcher: VersoWebviewDispatcher {
327 id: webview_id,
328 context: self.clone(),
329 webview,
330 },
331 },
332 use_https_scheme: false,
333 }),
334 })
335 }
336
337 pub fn handle_close_window_request<F: FnMut(RunEvent<T>) + 'static>(
343 &self,
344 callback: &mut F,
345 id: WindowId,
346 force: bool,
347 ) -> bool {
348 let mut windows = self.windows.lock().unwrap();
349 let Some(window) = windows.get(&id) else {
350 return false;
351 };
352 let label = window.label.clone();
353 let on_window_event_listeners = window.on_window_event_listeners.clone();
354
355 if !force {
356 let (tx, rx) = channel();
357 let window_event = WindowEvent::CloseRequested {
358 signal_tx: tx.clone(),
359 };
360 for handler in on_window_event_listeners.lock().unwrap().values() {
361 handler(&window_event);
362 }
363 callback(RunEvent::WindowEvent {
364 label: label.clone(),
365 event: WindowEvent::CloseRequested { signal_tx: tx },
366 });
367
368 let should_prevent = matches!(rx.try_recv(), Ok(true));
369 if should_prevent {
370 return false;
371 }
372 }
373
374 let webview_weak = std::sync::Arc::downgrade(&window.webview);
375
376 windows.remove(&id);
377 callback(RunEvent::WindowEvent {
378 label,
379 event: WindowEvent::Destroyed,
380 });
381
382 on_window_event_listeners.lock().unwrap().clear();
385
386 if let Some(webview) = webview_weak.upgrade() {
387 log::warn!(
388 "The versoview controller reference count is not 0 on window close, \
389 there're leaks happening, shutting down this versoview instance regardless"
390 );
391 if let Err(error) = webview.lock().unwrap().exit() {
392 log::error!("Failed to exit the webview: {error}");
393 }
394 }
395
396 let is_empty = windows.is_empty();
397 if !is_empty {
398 return false;
399 }
400
401 let (tx, rx) = channel();
402 callback(RunEvent::ExitRequested { code: None, tx });
403
404 let recv = rx.try_recv();
405 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
406
407 !should_prevent
408 }
409}
410
411#[cfg(windows)]
418fn is_work_around_uri(uri: &str, http_or_https: &str, protocol: &str) -> bool {
419 uri.strip_prefix(http_or_https)
420 .and_then(|rest| rest.strip_prefix("://"))
421 .and_then(|rest| rest.strip_prefix(protocol))
422 .and_then(|rest| rest.strip_prefix("."))
423 .is_some()
424}
425
426#[cfg(windows)]
430fn revert_custom_protocol_work_around(
431 uri: &str,
432 http_or_https: &'static str,
433 protocol: &str,
434) -> std::result::Result<http::Uri, http::uri::InvalidUri> {
435 uri.replace(
436 &work_around_uri_prefix(http_or_https, protocol),
437 &format!("{protocol}://"),
438 )
439 .parse()
440}
441
442#[cfg(windows)]
443fn work_around_uri_prefix(http_or_https: &str, protocol: &str) -> String {
444 format!("{http_or_https}://{protocol}.")
445}
446
447impl<T: UserEvent> Debug for RuntimeContext<T> {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 f.debug_struct("RuntimeContext").finish()
450 }
451}
452
453#[derive(Debug, Clone)]
455pub struct VersoRuntimeHandle<T: UserEvent> {
456 context: RuntimeContext<T>,
457}
458
459impl<T: UserEvent> RuntimeHandle<T> for VersoRuntimeHandle<T> {
460 type Runtime = VersoRuntime<T>;
461
462 fn create_proxy(&self) -> EventProxy<T> {
463 EventProxy(self.context.event_proxy.clone())
464 }
465
466 #[cfg(target_os = "macos")]
468 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
469 fn set_activation_policy(
470 &self,
471 activation_policy: tauri_runtime::ActivationPolicy,
472 ) -> Result<()> {
473 Ok(())
474 }
475
476 #[cfg(target_os = "macos")]
478 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
479 fn set_dock_visibility(&self, visible: bool) -> Result<()> {
480 Ok(())
481 }
482
483 fn request_exit(&self, code: i32) -> Result<()> {
484 self.context.send_message(Message::RequestExit(code))
485 }
486
487 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
492 &self,
493 pending: PendingWindow<T, Self::Runtime>,
494 after_window_creation: Option<F>,
495 ) -> Result<DetachedWindow<T, Self::Runtime>> {
496 self.context.create_window(pending, after_window_creation)
497 }
498
499 fn create_webview(
501 &self,
502 window_id: WindowId,
503 pending: PendingWebview<T, Self::Runtime>,
504 ) -> Result<DetachedWebview<T, Self::Runtime>> {
505 Err(tauri_runtime::Error::CreateWindow)
506 }
507
508 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
510 self.context.run_on_main_thread(f)
511 }
512
513 fn primary_monitor(&self) -> Option<Monitor> {
514 self.context
515 .run_on_main_thread_with_event_loop(|e| e.tauri_primary_monitor())
516 .ok()
517 .flatten()
518 }
519
520 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
521 self.context
522 .run_on_main_thread_with_event_loop(move |e| e.tauri_monitor_from_point(x, y))
523 .ok()
524 .flatten()
525 }
526
527 fn available_monitors(&self) -> Vec<Monitor> {
528 self.context
529 .run_on_main_thread_with_event_loop(|e| e.tauri_available_monitors())
530 .unwrap()
531 }
532
533 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
534 self.context
535 .run_on_main_thread_with_event_loop(|e| e.tauri_cursor_position())?
536 }
537
538 fn set_theme(&self, theme: Option<Theme>) {
539 *self.context.prefered_theme.lock().unwrap() = theme;
540 for window in self.context.windows.lock().unwrap().values() {
541 if let Err(error) = window
542 .webview
543 .lock()
544 .unwrap()
545 .set_theme(theme.map(to_verso_theme))
546 {
547 log::error!("Failed to set the theme for webview: {error}");
548 }
549 }
550 let _ = self
551 .context
552 .run_on_main_thread_with_event_loop(move |e| e.set_theme(theme.map(to_tao_theme)));
553 }
554
555 #[cfg(target_os = "macos")]
557 fn show(&self) -> Result<()> {
558 Ok(())
559 }
560
561 #[cfg(target_os = "macos")]
563 fn hide(&self) -> Result<()> {
564 Ok(())
565 }
566
567 fn display_handle(
569 &self,
570 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError>
571 {
572 Err(raw_window_handle::HandleError::NotSupported)
573 }
574
575 #[cfg(any(target_os = "macos", target_os = "ios"))]
577 #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
578 fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
579 &self,
580 cb: F,
581 ) -> Result<()> {
582 Ok(())
583 }
584
585 #[cfg(any(target_os = "macos", target_os = "ios"))]
587 #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
588 fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
589 &self,
590 uuid: [u8; 16],
591 cb: F,
592 ) -> Result<()> {
593 Ok(())
594 }
595}
596
597#[derive(Debug, Clone)]
598pub struct EventProxy<T: UserEvent>(TaoEventLoopProxy<Message<T>>);
599
600impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
601 fn send_event(&self, event: T) -> Result<()> {
602 self.0
603 .send_event(Message::UserEvent(event))
604 .map_err(|_| Error::FailedToSendMessage)
605 }
606}
607
608#[derive(Debug)]
610pub struct VersoRuntime<T: UserEvent = tauri::EventLoopMessage> {
611 pub context: RuntimeContext<T>,
612 event_loop: EventLoop<Message<T>>,
613}
614
615impl<T: UserEvent> VersoRuntime<T> {
616 fn init(event_loop: EventLoop<Message<T>>) -> Self {
617 let context = RuntimeContext {
618 windows: Default::default(),
619 prefered_theme: Arc::default(),
620 event_proxy: event_loop.create_proxy(),
621 main_thread: DispatcherMainThreadContext {
622 window_target: event_loop.deref().clone(),
623 },
624 main_thread_id: current_thread().id(),
625 next_window_id: Default::default(),
626 next_webview_id: Default::default(),
627 next_window_event_id: Default::default(),
628 next_webview_event_id: Default::default(),
629 };
630 Self {
631 context,
632 event_loop,
633 }
634 }
635
636 fn init_with_builder(
637 mut event_loop_builder: EventLoopBuilder<Message<T>>,
638 args: RuntimeInitArgs,
639 ) -> Self {
640 #[cfg(windows)]
641 if let Some(hook) = args.msg_hook {
642 use tao::platform::windows::EventLoopBuilderExtWindows;
643 event_loop_builder.with_msg_hook(hook);
644 }
645
646 #[cfg(any(
647 target_os = "linux",
648 target_os = "dragonfly",
649 target_os = "freebsd",
650 target_os = "netbsd",
651 target_os = "openbsd"
652 ))]
653 if let Some(app_id) = args.app_id {
654 use tao::platform::unix::EventLoopBuilderExtUnix;
655 event_loop_builder.with_app_id(app_id);
656 }
657 Self::init(event_loop_builder.build())
658 }
659}
660
661impl<T: UserEvent> Runtime<T> for VersoRuntime<T> {
662 type WindowDispatcher = VersoWindowDispatcher<T>;
663 type WebviewDispatcher = VersoWebviewDispatcher<T>;
664 type Handle = VersoRuntimeHandle<T>;
665 type EventLoopProxy = EventProxy<T>;
666
667 fn new(args: RuntimeInitArgs) -> Result<Self> {
670 let event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
671 Ok(Self::init_with_builder(event_loop_builder, args))
672 }
673
674 #[cfg(any(windows, target_os = "linux"))]
677 fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
678 let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
679 #[cfg(target_os = "linux")]
680 use tao::platform::unix::EventLoopBuilderExtUnix;
681 #[cfg(windows)]
682 use tao::platform::windows::EventLoopBuilderExtWindows;
683 event_loop_builder.with_any_thread(true);
684 Ok(Self::init_with_builder(event_loop_builder, args))
685 }
686
687 fn create_proxy(&self) -> EventProxy<T> {
688 EventProxy(self.event_loop.create_proxy())
689 }
690
691 fn handle(&self) -> Self::Handle {
692 VersoRuntimeHandle {
693 context: self.context.clone(),
694 }
695 }
696
697 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
702 &self,
703 pending: PendingWindow<T, Self>,
704 after_window_creation: Option<F>,
705 ) -> Result<DetachedWindow<T, Self>> {
706 self.context.create_window(pending, after_window_creation)
707 }
708
709 fn create_webview(
711 &self,
712 window_id: WindowId,
713 pending: PendingWebview<T, Self>,
714 ) -> Result<DetachedWebview<T, Self>> {
715 Err(tauri_runtime::Error::CreateWindow)
716 }
717
718 fn primary_monitor(&self) -> Option<Monitor> {
719 self.event_loop.tauri_primary_monitor()
720 }
721
722 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
723 self.event_loop.tauri_monitor_from_point(x, y)
724 }
725
726 fn available_monitors(&self) -> Vec<Monitor> {
727 self.event_loop.tauri_available_monitors()
728 }
729
730 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
731 self.event_loop.tauri_cursor_position()
732 }
733
734 fn set_theme(&self, theme: Option<Theme>) {
735 *self.context.prefered_theme.lock().unwrap() = theme;
736 for window in self.context.windows.lock().unwrap().values() {
737 if let Err(error) = window
738 .webview
739 .lock()
740 .unwrap()
741 .set_theme(theme.map(to_verso_theme))
742 {
743 log::error!("Failed to set the theme for webview: {error}");
744 }
745 }
746 self.event_loop.set_theme(theme.map(to_tao_theme));
747 }
748
749 #[cfg(target_os = "macos")]
751 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
752 fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {}
753
754 #[cfg(target_os = "macos")]
756 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
757 fn show(&self) {}
758
759 #[cfg(target_os = "macos")]
761 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
762 fn hide(&self) {}
763
764 #[cfg(target_os = "macos")]
766 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
767 fn set_dock_visibility(&mut self, visible: bool) {}
768
769 fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {}
771
772 fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F) {}
774
775 fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) {
776 let exit_code = self.run_return(callback);
777 }
779
780 fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, mut callback: F) -> i32 {
781 self.event_loop
782 .run_return(|event, event_loop, control_flow| {
783 if *control_flow != ControlFlow::Exit {
784 *control_flow = ControlFlow::Wait;
785 }
786
787 match event {
788 TaoEvent::NewEvents(StartCause::Init) => {
789 callback(RunEvent::Ready);
790 }
791 TaoEvent::NewEvents(StartCause::Poll) => {
792 callback(RunEvent::Resumed);
793 }
794 TaoEvent::MainEventsCleared => {
795 callback(RunEvent::MainEventsCleared);
796 }
797 TaoEvent::LoopDestroyed => {
798 callback(RunEvent::Exit);
799 }
800 TaoEvent::UserEvent(user_event) => match user_event {
801 Message::Task(p) => p(),
802 Message::TaskWithEventLoop(p) => p(event_loop),
803 Message::CloseWindow(id) => {
804 let should_exit =
805 self.context
806 .handle_close_window_request(&mut callback, id, false);
807 if should_exit {
808 *control_flow = ControlFlow::Exit;
809 }
810 }
811 Message::DestroyWindow(id) => {
812 let should_exit =
813 self.context
814 .handle_close_window_request(&mut callback, id, true);
815 if should_exit {
816 *control_flow = ControlFlow::Exit;
817 }
818 }
819 Message::RequestExit(code) => {
820 let (tx, rx) = channel();
821 callback(RunEvent::ExitRequested {
822 code: Some(code),
823 tx,
824 });
825
826 let recv = rx.try_recv();
827 let should_prevent =
828 matches!(recv, Ok(ExitRequestedEventAction::Prevent));
829
830 if !should_prevent {
831 *control_flow = ControlFlow::Exit;
832 }
833 }
834 Message::UserEvent(user_event) => callback(RunEvent::UserEvent(user_event)),
835 },
836 _ => {}
837 }
838 })
839 }
840}