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