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
mod default_program_icon {
    include!(concat!(env!("OUT_DIR"), "/default_program_icon.rs"));
}

use {
    serde::{Deserialize, Serialize},
    std::convert::{AsRef, TryFrom},
    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> {
    pub height: u32,
    pub pixels: &'a [u8],
    pub width: u32,
}

impl Icon<'static> {
    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.
///
/// 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> {
    pub author: &'static str,
    pub fullscreen: bool,
    pub icon: Option<Icon<'b>>,
    pub name: &'static str,
    pub resizable: bool,
    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
    /// covering approximately 75% of the desktop area.
    ///
    /// 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
    }
}

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()
    }
}