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 Err(raw_window_handle::HandleError::NotSupported)
572 }
573
574 #[cfg(any(target_os = "macos", target_os = "ios"))]
576 #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
577 fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
578 &self,
579 cb: F,
580 ) -> Result<()> {
581 Ok(())
582 }
583
584 #[cfg(any(target_os = "macos", target_os = "ios"))]
586 #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
587 fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
588 &self,
589 uuid: [u8; 16],
590 cb: F,
591 ) -> Result<()> {
592 Ok(())
593 }
594}
595
596#[derive(Debug, Clone)]
597pub struct EventProxy<T: UserEvent>(TaoEventLoopProxy<Message<T>>);
598
599impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
600 fn send_event(&self, event: T) -> Result<()> {
601 self.0
602 .send_event(Message::UserEvent(event))
603 .map_err(|_| Error::FailedToSendMessage)
604 }
605}
606
607#[derive(Debug)]
609pub struct VersoRuntime<T: UserEvent = tauri::EventLoopMessage> {
610 pub context: RuntimeContext<T>,
611 event_loop: EventLoop<Message<T>>,
612}
613
614impl<T: UserEvent> VersoRuntime<T> {
615 fn init(event_loop: EventLoop<Message<T>>) -> Self {
616 let context = RuntimeContext {
617 windows: Default::default(),
618 prefered_theme: Arc::default(),
619 event_proxy: event_loop.create_proxy(),
620 main_thread: DispatcherMainThreadContext {
621 window_target: event_loop.deref().clone(),
622 },
623 main_thread_id: current_thread().id(),
624 next_window_id: Default::default(),
625 next_webview_id: Default::default(),
626 next_window_event_id: Default::default(),
627 next_webview_event_id: Default::default(),
628 };
629 Self {
630 context,
631 event_loop,
632 }
633 }
634
635 fn init_with_builder(
636 mut event_loop_builder: EventLoopBuilder<Message<T>>,
637 args: RuntimeInitArgs,
638 ) -> Self {
639 #[cfg(windows)]
640 if let Some(hook) = args.msg_hook {
641 use tao::platform::windows::EventLoopBuilderExtWindows;
642 event_loop_builder.with_msg_hook(hook);
643 }
644
645 #[cfg(any(
646 target_os = "linux",
647 target_os = "dragonfly",
648 target_os = "freebsd",
649 target_os = "netbsd",
650 target_os = "openbsd"
651 ))]
652 if let Some(app_id) = args.app_id {
653 use tao::platform::unix::EventLoopBuilderExtUnix;
654 event_loop_builder.with_app_id(app_id);
655 }
656 Self::init(event_loop_builder.build())
657 }
658}
659
660impl<T: UserEvent> Runtime<T> for VersoRuntime<T> {
661 type WindowDispatcher = VersoWindowDispatcher<T>;
662 type WebviewDispatcher = VersoWebviewDispatcher<T>;
663 type Handle = VersoRuntimeHandle<T>;
664 type EventLoopProxy = EventProxy<T>;
665
666 fn new(args: RuntimeInitArgs) -> Result<Self> {
669 let event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
670 Ok(Self::init_with_builder(event_loop_builder, args))
671 }
672
673 #[cfg(any(windows, target_os = "linux"))]
676 fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
677 let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
678 #[cfg(target_os = "linux")]
679 use tao::platform::unix::EventLoopBuilderExtUnix;
680 #[cfg(windows)]
681 use tao::platform::windows::EventLoopBuilderExtWindows;
682 event_loop_builder.with_any_thread(true);
683 Ok(Self::init_with_builder(event_loop_builder, args))
684 }
685
686 fn create_proxy(&self) -> EventProxy<T> {
687 EventProxy(self.event_loop.create_proxy())
688 }
689
690 fn handle(&self) -> Self::Handle {
691 VersoRuntimeHandle {
692 context: self.context.clone(),
693 }
694 }
695
696 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
701 &self,
702 pending: PendingWindow<T, Self>,
703 after_window_creation: Option<F>,
704 ) -> Result<DetachedWindow<T, Self>> {
705 self.context.create_window(pending, after_window_creation)
706 }
707
708 fn create_webview(
710 &self,
711 window_id: WindowId,
712 pending: PendingWebview<T, Self>,
713 ) -> Result<DetachedWebview<T, Self>> {
714 Err(tauri_runtime::Error::CreateWindow)
715 }
716
717 fn primary_monitor(&self) -> Option<Monitor> {
718 self.event_loop.tauri_primary_monitor()
719 }
720
721 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
722 self.event_loop.tauri_monitor_from_point(x, y)
723 }
724
725 fn available_monitors(&self) -> Vec<Monitor> {
726 self.event_loop.tauri_available_monitors()
727 }
728
729 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
730 self.event_loop.tauri_cursor_position()
731 }
732
733 fn set_theme(&self, theme: Option<Theme>) {
734 *self.context.prefered_theme.lock().unwrap() = theme;
735 for window in self.context.windows.lock().unwrap().values() {
736 if let Err(error) = window
737 .webview
738 .lock()
739 .unwrap()
740 .set_theme(theme.map(to_verso_theme))
741 {
742 log::error!("Failed to set the theme for webview: {error}");
743 }
744 }
745 self.event_loop.set_theme(theme.map(to_tao_theme));
746 }
747
748 #[cfg(target_os = "macos")]
750 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
751 fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {}
752
753 #[cfg(target_os = "macos")]
755 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
756 fn show(&self) {}
757
758 #[cfg(target_os = "macos")]
760 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
761 fn hide(&self) {}
762
763 #[cfg(target_os = "macos")]
765 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
766 fn set_dock_visibility(&mut self, visible: bool) {}
767
768 fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {}
770
771 fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F) {}
773
774 fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) {
775 let exit_code = self.run_return(callback);
776 }
778
779 fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, mut callback: F) -> i32 {
780 self.event_loop
781 .run_return(|event, event_loop, control_flow| {
782 if *control_flow != ControlFlow::Exit {
783 *control_flow = ControlFlow::Wait;
784 }
785
786 match event {
787 TaoEvent::NewEvents(StartCause::Init) => {
788 callback(RunEvent::Ready);
789 }
790 TaoEvent::NewEvents(StartCause::Poll) => {
791 callback(RunEvent::Resumed);
792 }
793 TaoEvent::MainEventsCleared => {
794 callback(RunEvent::MainEventsCleared);
795 }
796 TaoEvent::LoopDestroyed => {
797 callback(RunEvent::Exit);
798 }
799 TaoEvent::UserEvent(user_event) => match user_event {
800 Message::Task(p) => p(),
801 Message::TaskWithEventLoop(p) => p(event_loop),
802 Message::CloseWindow(id) => {
803 let should_exit =
804 self.context
805 .handle_close_window_request(&mut callback, id, false);
806 if should_exit {
807 *control_flow = ControlFlow::Exit;
808 }
809 }
810 Message::DestroyWindow(id) => {
811 let should_exit =
812 self.context
813 .handle_close_window_request(&mut callback, id, true);
814 if should_exit {
815 *control_flow = ControlFlow::Exit;
816 }
817 }
818 Message::RequestExit(code) => {
819 let (tx, rx) = channel();
820 callback(RunEvent::ExitRequested {
821 code: Some(code),
822 tx,
823 });
824
825 let recv = rx.try_recv();
826 let should_prevent =
827 matches!(recv, Ok(ExitRequestedEventAction::Prevent));
828
829 if !should_prevent {
830 *control_flow = ControlFlow::Exit;
831 }
832 }
833 Message::UserEvent(user_event) => callback(RunEvent::UserEvent(user_event)),
834 },
835 _ => {}
836 }
837 })
838 }
839}