quicksilver/
run.rs

1use crate::geom::{Transform, Vector};
2use crate::graphics::Graphics;
3use crate::input::Input;
4use std::error::Error;
5use std::future::Future;
6
7/// Initial window and behavior options
8pub struct Settings {
9    /// The size of the window
10    pub size: Vector,
11    /// If the cursor should be visible over the application, or if the cursor should be hidden
12    pub cursor_icon: Option<crate::CursorIcon>,
13    /// If the application should be fullscreen
14    pub fullscreen: bool,
15    /// The icon on the window or the favicon on the tab
16    pub icon_path: Option<&'static str>,
17    /// How many samples to do for MSAA
18    ///
19    /// By default it is None; if it is Some, it should be a non-zero power of two
20    ///
21    /// Does nothing on web currently
22    pub multisampling: Option<u16>,
23    /// Enable or disable vertical sync
24    ///
25    /// Does nothing on web
26    pub vsync: bool,
27    /// If the window can be resized by the user
28    ///
29    /// Does nothing on web
30    pub resizable: bool,
31    /// The title of your application
32    pub title: &'static str,
33    /// The severity level of logs to show
34    ///
35    /// By default, it is set to Warn
36    pub log_level: log::Level,
37    /// On desktop, whether to assume the assets are in the 'static/' directory
38    ///
39    /// By default, this is on for comfortable parity between stdweb and desktop. If you know you
40    /// don't need that, feel free to toggle this off
41    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
61/// The entry point of a Quicksilver application
62///
63/// It provides your application (represented by an async closure or function) with a [`Window`],
64/// [`Graphics`] context, and [`Input`].
65///
66/// [`Graphics`]: crate::Graphics
67/// [`Window`]: crate::Window
68/// [`Input`]: crate::Input
69pub 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}