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