1pub mod reexports {
2 pub use winit;
3}
4
5use std::sync::Arc;
6
7use crate::{
8 config::LaunchConfig,
9 renderer::{
10 LaunchProxy,
11 NativeEvent,
12 NativeGenericEvent,
13 WinitRenderer,
14 },
15};
16mod accessibility;
17pub mod config;
18mod drivers;
19pub mod extensions;
20pub mod integration;
21pub mod plugins;
22pub mod renderer;
23#[cfg(feature = "tray")]
24mod tray_icon;
25mod window;
26mod winit_mappings;
27
28pub use extensions::*;
29use futures_util::task::{
30 ArcWake,
31 waker,
32};
33
34use crate::winit::event_loop::EventLoopProxy;
35
36pub mod winit {
37 pub use winit::*;
38}
39
40#[cfg(feature = "tray")]
41pub mod tray {
42 pub use tray_icon::*;
43
44 pub use crate::tray_icon::*;
45}
46
47pub fn launch(launch_config: LaunchConfig) {
52 #[cfg(all(not(debug_assertions), not(target_os = "android")))]
53 {
54 let run_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
55 launch_inner(launch_config);
56 }));
57 if let Err(panic_payload) = run_result {
58 let description = panic_payload
59 .downcast_ref::<&str>()
60 .map(|message| message.to_string())
61 .or_else(|| panic_payload.downcast_ref::<String>().cloned())
62 .unwrap_or_else(|| "The application panicked.".to_string());
63 rfd::MessageDialog::new()
64 .set_title("Fatal Error")
65 .set_description(&description)
66 .set_level(rfd::MessageLevel::Error)
67 .show();
68 std::process::exit(1);
69 }
70 }
71
72 #[cfg(any(debug_assertions, target_os = "android"))]
73 launch_inner(launch_config);
74}
75
76fn launch_inner(mut launch_config: LaunchConfig) {
77 use std::collections::HashMap;
78
79 use freya_core::integration::*;
80 use freya_engine::prelude::{
81 FontCollection,
82 FontMgr,
83 TypefaceFontProvider,
84 };
85 use winit::event_loop::EventLoop;
86
87 let event_loop = launch_config.event_loop.take().unwrap_or_else(|| {
88 EventLoop::<NativeEvent>::with_user_event()
89 .build()
90 .expect("Failed to create event loop.")
91 });
92
93 let proxy = event_loop.create_proxy();
94
95 let mut font_collection = FontCollection::new();
96 let def_mgr = FontMgr::default();
97 let font_mgr = FontMgr::custom_empty().unwrap_or_default();
98 let mut provider = TypefaceFontProvider::new();
99 for (font_name, font_data) in launch_config.embedded_fonts {
100 let typeface = font_mgr
101 .new_from_data(&font_data, None)
102 .unwrap_or_else(|| panic!("Failed to load font {font_name}."));
103 provider.register_typeface(typeface, Some(font_name.as_ref()));
104 }
105 let font_mgr: FontMgr = provider.into();
106 font_collection.set_default_font_manager(def_mgr, None);
107 font_collection.set_dynamic_font_manager(font_mgr.clone());
108 font_collection.paragraph_cache_mut().turn_on(false);
109
110 let screen_reader = ScreenReader::new();
111
112 struct FuturesWaker(EventLoopProxy<NativeEvent>);
113
114 impl ArcWake for FuturesWaker {
115 fn wake_by_ref(arc_self: &Arc<Self>) {
116 _ = arc_self
117 .0
118 .send_event(NativeEvent::Generic(NativeGenericEvent::PollFutures));
119 }
120 }
121
122 let waker = waker(Arc::new(FuturesWaker(proxy.clone())));
123
124 #[cfg(feature = "hotreload")]
125 freya_core::hotreload::connect_subsecond();
126
127 let mut renderer = WinitRenderer {
128 windows: HashMap::default(),
129 #[cfg(feature = "tray")]
130 tray: launch_config.tray,
131 #[cfg(all(feature = "tray", not(target_os = "linux")))]
132 tray_icon: None,
133 resumed: false,
134 futures: launch_config
135 .tasks
136 .into_iter()
137 .map(|task| task(LaunchProxy(proxy.clone())))
138 .collect::<Vec<_>>(),
139 proxy,
140 font_manager: font_mgr,
141 font_collection,
142 windows_configs: launch_config.windows_configs,
143 plugins: launch_config.plugins,
144 fallback_fonts: launch_config.fallback_fonts,
145 screen_reader,
146 waker,
147 exit_on_close: launch_config.exit_on_close,
148 gpu_resource_cache_limit: launch_config.gpu_resource_cache_limit,
149 };
150
151 #[cfg(feature = "tray")]
152 {
153 use crate::{
154 renderer::{
155 NativeTrayEvent,
156 NativeTrayEventAction,
157 },
158 tray::{
159 TrayIconEvent,
160 menu::MenuEvent,
161 },
162 };
163
164 let proxy = renderer.proxy.clone();
165 MenuEvent::set_event_handler(Some(move |event| {
166 let _ = proxy.send_event(NativeEvent::Tray(NativeTrayEvent {
167 action: NativeTrayEventAction::MenuEvent(event),
168 }));
169 }));
170 let proxy = renderer.proxy.clone();
171 TrayIconEvent::set_event_handler(Some(move |event| {
172 let _ = proxy.send_event(NativeEvent::Tray(NativeTrayEvent {
173 action: NativeTrayEventAction::TrayEvent(event),
174 }));
175 }));
176
177 #[cfg(target_os = "linux")]
178 if let Some(tray_icon) = renderer.tray.0.take() {
179 std::thread::spawn(move || {
180 if !gtk::is_initialized() {
181 if gtk::init().is_ok() {
182 tracing::debug!("Tray: GTK initialized");
183 } else {
184 tracing::error!("Tray: Failed to initialize GTK");
185 }
186 }
187
188 let _tray_icon = (tray_icon)();
189
190 gtk::main();
191 });
192 }
193 }
194
195 event_loop.run_app(&mut renderer).unwrap();
196}