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 plugins;
21pub mod renderer;
22#[cfg(feature = "tray")]
23mod tray_icon;
24mod window;
25mod winit_mappings;
26
27pub use extensions::*;
28use futures_util::task::{
29    ArcWake,
30    waker,
31};
32
33use crate::winit::event_loop::EventLoopProxy;
34
35pub mod winit {
36    pub use winit::*;
37}
38
39#[cfg(feature = "tray")]
40pub mod tray {
41    pub use tray_icon::*;
42
43    pub use crate::tray_icon::*;
44}
45
46pub fn launch(launch_config: LaunchConfig) {
47    use std::collections::HashMap;
48
49    #[cfg(all(not(debug_assertions), not(target_os = "android")))]
50    {
51        let previous_hook = std::panic::take_hook();
52        std::panic::set_hook(Box::new(move |panic_info| {
53            rfd::MessageDialog::new()
54                .set_title("Fatal Error")
55                .set_description(&panic_info.to_string())
56                .set_level(rfd::MessageLevel::Error)
57                .show();
58            previous_hook(panic_info);
59            std::process::exit(1);
60        }));
61    }
62
63    use freya_core::integration::*;
64    use freya_engine::prelude::{
65        FontCollection,
66        FontMgr,
67        TypefaceFontProvider,
68    };
69    use winit::event_loop::EventLoop;
70
71    let mut event_loop_builder = EventLoop::<NativeEvent>::with_user_event();
72
73    let event_loop = event_loop_builder
74        .build()
75        .expect("Failed to create event loop.");
76
77    let proxy = event_loop.create_proxy();
78
79    let mut font_collection = FontCollection::new();
80    let def_mgr = FontMgr::default();
81    let mut provider = TypefaceFontProvider::new();
82    for (font_name, font_data) in launch_config.embedded_fonts {
83        let ft_type = def_mgr
84            .new_from_data(&font_data, None)
85            .unwrap_or_else(|| panic!("Failed to load font {font_name}."));
86        provider.register_typeface(ft_type, Some(font_name.as_ref()));
87    }
88    let font_mgr: FontMgr = provider.into();
89    font_collection.set_default_font_manager(def_mgr, None);
90    font_collection.set_dynamic_font_manager(font_mgr.clone());
91    font_collection.paragraph_cache_mut().turn_on(false);
92
93    let screen_reader = ScreenReader::new();
94
95    struct FuturesWaker(EventLoopProxy<NativeEvent>);
96
97    impl ArcWake for FuturesWaker {
98        fn wake_by_ref(arc_self: &Arc<Self>) {
99            _ = arc_self
100                .0
101                .send_event(NativeEvent::Generic(NativeGenericEvent::PollFutures));
102        }
103    }
104
105    let waker = waker(Arc::new(FuturesWaker(proxy.clone())));
106
107    let mut renderer = WinitRenderer {
108        windows: HashMap::default(),
109        #[cfg(feature = "tray")]
110        tray: launch_config.tray,
111        #[cfg(all(feature = "tray", not(target_os = "linux")))]
112        tray_icon: None,
113        resumed: false,
114        futures: launch_config
115            .tasks
116            .into_iter()
117            .map(|task| task(LaunchProxy(proxy.clone())))
118            .collect::<Vec<_>>(),
119        proxy,
120        font_manager: font_mgr,
121        font_collection,
122        windows_configs: launch_config.windows_configs,
123        plugins: launch_config.plugins,
124        fallback_fonts: launch_config.fallback_fonts,
125        screen_reader,
126        waker,
127    };
128
129    #[cfg(feature = "tray")]
130    {
131        use crate::{
132            renderer::{
133                NativeTrayEvent,
134                NativeTrayEventAction,
135            },
136            tray::{
137                TrayIconEvent,
138                menu::MenuEvent,
139            },
140        };
141
142        let proxy = renderer.proxy.clone();
143        MenuEvent::set_event_handler(Some(move |event| {
144            let _ = proxy.send_event(NativeEvent::Tray(NativeTrayEvent {
145                action: NativeTrayEventAction::MenuEvent(event),
146            }));
147        }));
148        let proxy = renderer.proxy.clone();
149        TrayIconEvent::set_event_handler(Some(move |event| {
150            let _ = proxy.send_event(NativeEvent::Tray(NativeTrayEvent {
151                action: NativeTrayEventAction::TrayEvent(event),
152            }));
153        }));
154
155        #[cfg(target_os = "linux")]
156        if let Some(tray_icon) = renderer.tray.0.take() {
157            std::thread::spawn(move || {
158                if !gtk::is_initialized() {
159                    if gtk::init().is_ok() {
160                        tracing::debug!("Tray: GTK initialized");
161                    } else {
162                        tracing::error!("Tray: Failed to initialize GTK");
163                    }
164                }
165
166                let _tray_icon = (tray_icon)();
167
168                gtk::main();
169            });
170        }
171    }
172
173    event_loop.run_app(&mut renderer).unwrap();
174}