1use crate::geom::{Transform, Vector};
2use crate::graphics::Graphics;
3use crate::input::Input;
4use std::error::Error;
5use std::future::Future;
6
7pub struct Settings {
9    pub size: Vector,
11    pub cursor_icon: Option<crate::CursorIcon>,
13    pub fullscreen: bool,
15    pub icon_path: Option<&'static str>,
17    pub multisampling: Option<u16>,
23    pub vsync: bool,
27    pub resizable: bool,
31    pub title: &'static str,
33    pub log_level: log::Level,
37    pub use_static_dir: bool,
42}
43
44impl Default for Settings {
45    fn default() -> Settings {
46        Settings {
47            size: Vector::new(1024.0, 768.0),
48            cursor_icon: Some(blinds::CursorIcon::Default),
49            fullscreen: false,
50            icon_path: None,
51            multisampling: None,
52            vsync: true,
53            resizable: false,
54            title: "",
55            log_level: log::Level::Warn,
56            use_static_dir: true,
57        }
58    }
59}
60
61pub fn run<E, F, T>(settings: Settings, app: F) -> !
70where
71    E: Into<Box<dyn Error + Send + Sync>>,
72    T: 'static + Future<Output = Result<(), E>>,
73    F: 'static + FnOnce(crate::Window, Graphics, Input) -> T,
74{
75    #[cfg(feature = "easy-log")]
76    set_logger(settings.log_level);
77
78    blinds::run_gl((&settings).into(), move |window, ctx, events| {
79        #[cfg(not(target_arch = "wasm32"))]
80        {
81            if settings.use_static_dir && std::env::set_current_dir("static").is_err() {
82                log::warn!("Warning: no asset directory found. Please place all your assets inside a directory called 'static' so they can be loaded");
83                log::warn!("Execution continuing, but any asset-not-found errors are likely due to the lack of a 'static' directory.")
84            }
85        }
86
87        let ctx = golem::Context::from_glow(ctx).unwrap();
88        let mut graphics = Graphics::new(ctx, settings.size).unwrap();
89        graphics.set_view(Transform::IDENTITY);
90
91        async {
92            match app(crate::Window(window), graphics, Input::new(events)).await {
93                Ok(()) => log::info!("Exited successfully"),
94                Err(err) => {
95                    let err = err.into();
96                    log::error!("Error: {:?}", err);
97                    panic!("{:?}", err);
98                }
99            }
100        }
101    });
102}
103
104#[cfg(feature = "easy-log")]
105fn set_logger(level: log::Level) {
106    #[cfg(target_arch = "wasm32")]
107    let _ = web_logger::try_init(web_logger::Config { level });
108    #[cfg(not(target_arch = "wasm32"))]
109    let _ = simple_logger::init_with_level(level);
110}
111
112impl From<&Settings> for blinds::Settings {
113    fn from(settings: &Settings) -> blinds::Settings {
114        blinds::Settings {
115            size: settings.size.into(),
116            cursor_icon: settings.cursor_icon,
117            fullscreen: settings.fullscreen,
118            icon_path: settings.icon_path,
119            multisampling: settings.multisampling,
120            vsync: settings.vsync,
121            resizable: settings.resizable,
122            title: settings.title,
123        }
124    }
125}
126
127impl From<blinds::Settings> for Settings {
128    fn from(settings: blinds::Settings) -> Settings {
129        Settings {
130            size: settings.size.into(),
131            cursor_icon: settings.cursor_icon,
132            fullscreen: settings.fullscreen,
133            icon_path: settings.icon_path,
134            multisampling: settings.multisampling,
135            vsync: settings.vsync,
136            resizable: settings.resizable,
137            title: settings.title,
138            ..Settings::default()
139        }
140    }
141}