1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use crate::geom::{Rectangle, Transform, Vector};
use crate::graphics::Graphics;
use crate::input::Input;
use std::error::Error;
use std::future::Future;

/// Initial window and behavior options
pub struct Settings {
    /// The size of the window
    pub size: Vector,
    /// If the cursor should be visible over the application, or if the cursor should be hidden
    pub cursor_icon: Option<crate::CursorIcon>,
    /// If the application should be fullscreen
    pub fullscreen: bool,
    /// The icon on the window or the favicon on the tab
    pub icon_path: Option<&'static str>,
    /// How many samples to do for MSAA
    ///
    /// By default it is None; if it is Some, it should be a non-zero power of two
    ///
    /// Does nothing on web currently
    pub multisampling: Option<u16>,
    /// Enable or disable vertical sync
    ///
    /// Does nothing on web
    pub vsync: bool,
    /// If the window can be resized by the user
    ///
    /// Does nothing on web
    pub resizable: bool,
    /// The title of your application
    pub title: &'static str,
    /// The severity level of logs to show
    ///
    /// By default, it is set to Warn
    pub log_level: log::Level,
    /// On desktop, whether to assume the assets are in the 'static/' directory
    ///
    /// By default, this is on for comfortable parity between stdweb and desktop. If you know you
    /// don't need that, feel free to toggle this off
    pub use_static_dir: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            size: Vector::new(1024.0, 768.0),
            cursor_icon: Some(blinds::CursorIcon::Default),
            fullscreen: false,
            icon_path: None,
            multisampling: None,
            vsync: true,
            resizable: false,
            title: "",
            log_level: log::Level::Warn,
            use_static_dir: true,
        }
    }
}

/// The entry point of a Quicksilver application
///
/// It provides your application (represented by an async closure or function) with a [`Window`],
/// [`Graphics`] context, and [`Input`].
///
/// [`Graphics`]: crate::Graphics
/// [`Window`]: crate::Window
/// [`Input`]: crate::Input
pub fn run<E, F, T>(settings: Settings, app: F) -> !
where
    E: Into<Box<dyn Error + Send + Sync>>,
    T: 'static + Future<Output = Result<(), E>>,
    F: 'static + FnOnce(crate::Window, Graphics, Input) -> T,
{
    #[cfg(feature = "easy-log")]
    set_logger(settings.log_level);

    let size = settings.size;
    let screen_region = Rectangle::new_sized(size);

    blinds::run_gl((&settings).into(), move |window, ctx, events| {
        #[cfg(not(target_arch = "wasm32"))]
        {
            if settings.use_static_dir && std::env::set_current_dir("static").is_err() {
                log::warn!("Warning: no asset directory found. Please place all your assets inside a directory called 'static' so they can be loaded");
                log::warn!("Execution continuing, but any asset-not-found errors are likely due to the lack of a 'static' directory.")
            }
        }

        let ctx = golem::Context::from_glow(ctx).unwrap();
        let mut graphics = Graphics::new(ctx).unwrap();
        graphics.set_projection(Transform::orthographic(screen_region));

        async {
            match app(crate::Window(window), graphics, Input::new(events)).await {
                Ok(()) => log::info!("Exited successfully"),
                Err(err) => {
                    let err = err.into();
                    log::error!("Error: {:?}", err);
                    panic!("{:?}", err);
                }
            }
        }
    });
}

#[cfg(feature = "easy-log")]
fn set_logger(level: log::Level) {
    #[cfg(target_arch = "wasm32")]
    web_logger::custom_init(web_logger::Config { level });
    #[cfg(not(target_arch = "wasm32"))]
    simple_logger::init_with_level(level).expect("A logger was already initialized");
}

impl From<&Settings> for blinds::Settings {
    fn from(settings: &Settings) -> blinds::Settings {
        blinds::Settings {
            size: settings.size.into(),
            cursor_icon: settings.cursor_icon,
            fullscreen: settings.fullscreen,
            icon_path: settings.icon_path,
            multisampling: settings.multisampling,
            vsync: settings.vsync,
            resizable: settings.resizable,
            title: settings.title,
        }
    }
}

impl From<blinds::Settings> for Settings {
    fn from(settings: blinds::Settings) -> Settings {
        Settings {
            size: settings.size.into(),
            cursor_icon: settings.cursor_icon,
            fullscreen: settings.fullscreen,
            icon_path: settings.icon_path,
            multisampling: settings.multisampling,
            vsync: settings.vsync,
            resizable: settings.resizable,
            title: settings.title,
            ..Settings::default()
        }
    }
}