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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
mod default_program_icon {
    include!(concat!(env!("OUT_DIR"), "/default_program_icon.rs"));
}

use {
    super::program_root,
    serde::{Deserialize, Serialize},
    std::{
        convert::{AsRef, TryFrom},
        io::Error,
        path::PathBuf,
    },
    winit::window::{BadIcon, Icon as WinitIcon},
};

const DEFAULT_AUTHOR: &str = "screen-13";
const DEFAULT_NAME: &str = "default";

/// A small picture which represents the program, to be used by the operating system in different
/// ways on each platform. Pixels are RGBA formatted.
#[derive(Debug, Deserialize, Serialize)]
pub struct Icon<'a> {
    /// Icon height in pixels.
    pub height: u32,

    /// Array of RGBA-formatted pixel data.
    ///
    /// The length is four times the number of pixels.
    pub pixels: &'a [u8],

    /// Icon width in pixels.
    pub width: u32,
}

impl Icon<'static> {
    /// A fantastic icon chosen based on the order in which it was generated. 🗿
    pub const DEFAULT: Self = Self {
        height: default_program_icon::HEIGHT,
        pixels: &default_program_icon::PIXELS,
        width: default_program_icon::WIDTH,
    };
}

impl Default for Icon<'static> {
    fn default() -> Self {
        Self::DEFAULT
    }
}

impl<'a> TryFrom<&'a Icon<'_>> for WinitIcon {
    type Error = BadIcon;

    fn try_from(val: &'a Icon) -> Result<Self, Self::Error> {
        Self::from_rgba(val.pixels.to_owned(), val.width, val.height)
    }
}

/// Program is the required information to start an event loop, and therefore an `Engine`.
///
/// Remarks: The fullscreen/windowed setting this program describes may not be what Screen 13
/// chooses at runtime if there is a previously written configuration file present.
#[derive(Debug, Deserialize, Serialize)]
pub struct Program<'a, 'b> {
    /// Program author, or company.
    pub author: &'static str,

    /// Whether the program uses a full-screen video mode or not.
    pub fullscreen: bool,

    /// Program window icon, if set.
    pub icon: Option<Icon<'b>>,

    /// Program name, or title.
    pub name: &'static str,

    /// Whether the program window is resizable or not, while in window mode.
    ///
    /// Has no effect while in fullscreen mode.
    pub resizable: bool,

    /// Program window title.
    ///
    /// This is what is shown to the user.
    pub title: &'a str,
}

impl Program<'static, 'static> {
    /// A default program description, with a fullscreen setting.
    ///
    /// This is most useful for small examples and demos. Real programs should
    /// fill in all the info manually.
    pub const FULLSCREEN: Program<'static, 'static> = Program {
        author: DEFAULT_AUTHOR,
        fullscreen: true,
        icon: Some(Icon::DEFAULT),
        name: DEFAULT_NAME,
        resizable: true,
        title: DEFAULT_NAME,
    };

    /// A default program description, with a window mode setting.
    ///
    /// This is most useful for small examples and demos. Real programs should
    /// fill in all the info manually.
    pub const WINDOW: Program<'static, 'static> = Program {
        author: DEFAULT_AUTHOR,
        fullscreen: false,
        icon: Some(Icon::DEFAULT),
        name: DEFAULT_NAME,
        resizable: true,
        title: DEFAULT_NAME,
    };
}

impl Program<'_, '_> {
    /// Creates a new Program description.
    ///
    /// By default the program will be fullscreen; use the builder functions to change this and
    /// make other important choices.
    ///
    /// Remarks: Programs running in windowed mode automatically select an appropriately sized
    /// and placed window. Current logic provides a window centered on the primary display at HD
    /// resolution.
    ///
    /// Remarks: When in debug mode the default is windowed mode.
    pub const fn new(name: &'static str, author: &'static str) -> Self {
        #[cfg(not(debug_assertions))]
        let fullscreen = true;

        #[cfg(debug_assertions)]
        let fullscreen = false;

        Self {
            author,
            fullscreen,
            icon: None,
            name,
            resizable: true,
            title: name,
        }
    }

    const fn new_default() -> Self {
        Self::new(DEFAULT_NAME, DEFAULT_AUTHOR)
    }

    /// Sets whether the program starts as fullscreen of in window mode.
    pub const fn with_fullscreen(self) -> Self {
        self.with_fullscreen_is(true)
    }

    /// Sets whether the program starts as fullscreen of in window mode.
    pub const fn with_fullscreen_is(mut self, fullscreen: bool) -> Self {
        self.fullscreen = fullscreen;
        self
    }

    /// Sets the program name and program author (also known as publisher). These values are used
    /// for multiple purposes, including locating configuration files.
    pub const fn with_name_author(mut self, name: &'static str, author: &'static str) -> Self {
        self.author = author;
        self.name = name;
        self
    }

    /// Sets whether the window is resizable or not.
    pub const fn with_resizable(self) -> Self {
        self.with_resizable_is(true)
    }

    /// Sets whether the window is resizable or not.
    pub const fn with_resizable_is(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Sets whether the program starts in window mode instead of as fullscreen.
    pub const fn with_window(self) -> Self {
        self.with_window_is(true)
    }

    /// Sets whether the program starts in window mode instead of as fullscreen.
    pub const fn with_window_is(self, window: bool) -> Self {
        self.with_fullscreen_is(!window)
    }

    /// Clears the previously set window icon.
    pub fn without_icon(mut self) -> Self {
        self.icon = None;
        self
    }

    /// Gets the filesystem root for this program. The returned path is a good place to store
    /// program configuration and data on a per-user basis.
    pub fn root(&self) -> Result<PathBuf, Error> {
        program_root(self)
    }
}

impl<'a> Program<'a, '_> {
    /// Sets the window title, separately from the program name which is used internally to cache
    /// configuration changes.
    pub const fn with_title(mut self, title: &'a str) -> Self {
        self.title = title;
        self
    }
}

impl<'b> Program<'_, 'b> {
    /// Sets the window icon. The icon must be an rgba formatted pixel array, and must be square.
    pub fn with_icon(self, icon: Icon<'b>) -> Self {
        self.with_icon_is(Some(icon))
    }

    /// Sets the window icon. The icon must be an rgba formatted pixel array, and must be square.
    pub fn with_icon_is(mut self, icon: Option<Icon<'b>>) -> Self {
        self.icon = icon;
        self
    }
}

impl<'a, 'b> AsRef<Program<'a, 'b>> for Program<'a, 'b> {
    fn as_ref(&self) -> &Self {
        self
    }
}

impl Default for Program<'_, '_> {
    fn default() -> Self {
        Self::new_default()
    }
}