Skip to main content

freya_winit/
lib.rs

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
47/// Launch the application.
48///
49/// If a custom event loop was provided via [`LaunchConfig::with_event_loop`], it will be used.
50/// Otherwise a default one is created.
51pub 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}