rich_sdl2_rust/video/window/
builder.rs

1#![allow(clippy::unnecessary_cast)]
2
3use bitflags::bitflags;
4use std::ffi::CString;
5use std::ptr::NonNull;
6use typed_builder::TypedBuilder;
7
8use super::{Window, WindowContextKind, WindowFormat};
9use crate::{bind, Sdl, Video};
10
11/// A coordinate value for the window position.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct WindowCoord {
14    coord: i32,
15}
16
17impl WindowCoord {
18    /// Constructs from coord value. Must be in `-16384..=16384`.
19    ///
20    /// # Panics
21    ///
22    /// Panics if `coord` is not in `-16384..=16384`.
23    #[must_use]
24    pub fn coord(coord: i32) -> Self {
25        const MAX: i32 = 16384;
26        assert!((-MAX..=MAX).contains(&coord));
27        Self { coord }
28    }
29    /// Constructs the undefined coordinate.
30    #[must_use]
31    pub const fn undefined() -> Self {
32        Self {
33            coord: 0x1FFF0000, // SDL_WINDOWPOS_UNDEFINED
34        }
35    }
36    /// Constructs the centered coordinate.
37    #[must_use]
38    pub const fn centered() -> Self {
39        Self {
40            coord: 0x2FFF0000, // SDL_WINDOWPOS_CENTERED
41        }
42    }
43    pub(super) fn into_arg(self) -> std::os::raw::c_int {
44        self.coord
45    }
46}
47
48/// A builder for the [`Window`].
49#[derive(Debug, TypedBuilder)]
50pub struct WindowBuilder {
51    #[builder(default = "Untitled".into(), setter(into))]
52    title: String,
53    #[builder(default = WindowCoord::centered())]
54    x: WindowCoord,
55    #[builder(default = WindowCoord::centered())]
56    y: WindowCoord,
57    #[builder(default = 640)]
58    width: u32,
59    #[builder(default = 480)]
60    height: u32,
61    #[builder(default = WindowFormat::Normal)]
62    format: WindowFormat,
63    #[builder(default = WindowContextKind::Software)]
64    context_kind: WindowContextKind,
65    #[builder(default)]
66    hidden: bool,
67    #[builder(default)]
68    allow_high_dpi: bool,
69    #[builder(default)]
70    borderless: bool,
71    #[builder(default)]
72    resizable: bool,
73}
74
75impl WindowBuilder {
76    /// Builds the window.
77    #[must_use]
78    pub fn new_window<'video>(self, video: &'video Video) -> Window<'video> {
79        if self.context_kind == WindowContextKind::Vulkan {
80            let ret = unsafe { bind::SDL_Vulkan_LoadLibrary(std::ptr::null()) };
81            if ret == -1 {
82                Sdl::error_then_panic("loading vulkan library from SDL_VULKAN_LIBRARY");
83            }
84        }
85
86        let flags = self.calc_flags();
87
88        use std::os::raw::c_int;
89        let cstr = CString::new(self.title).unwrap_or_default();
90        let raw = unsafe {
91            bind::SDL_CreateWindow(
92                cstr.as_ptr(),
93                self.x.into_arg(),
94                self.y.into_arg(),
95                self.width as c_int,
96                self.height as c_int,
97                flags,
98            )
99        };
100        NonNull::new(raw).map_or_else(
101            || Sdl::error_then_panic("Sdl window"),
102            move |window| Window { window, video },
103        )
104    }
105
106    fn calc_flags(&self) -> u32 {
107        let mut flags = WindowFlags::empty();
108        flags |= match self.format {
109            WindowFormat::Normal => WindowFlags::empty(),
110            WindowFormat::Maximized => WindowFlags::MAXIMIZED,
111            WindowFormat::Minimized => WindowFlags::MINIMIZED,
112            WindowFormat::FullScreen => WindowFlags::FULLSCREEN,
113            WindowFormat::FullScreenWithCurrentDesktop => WindowFlags::FULLSCREEN_DESKTOP,
114        };
115        flags |= match self.context_kind {
116            WindowContextKind::Software => WindowFlags::empty(),
117            WindowContextKind::OpenGl => WindowFlags::OPENGL,
118            WindowContextKind::Vulkan => WindowFlags::VULKAN,
119            WindowContextKind::Metal => WindowFlags::METAL,
120        };
121        if self.hidden {
122            flags |= WindowFlags::HIDDEN;
123        }
124        if self.allow_high_dpi {
125            flags |= WindowFlags::ALLOW_HIGHDPI;
126        }
127        if self.borderless {
128            flags |= WindowFlags::BORDERLESS;
129        }
130        if self.resizable {
131            flags |= WindowFlags::RESIZABLE;
132        }
133        flags.bits()
134    }
135}
136
137bitflags! {
138    /// A flag for [`Window`].
139    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140    pub(super) struct WindowFlags: u32 {
141        const FULLSCREEN = bind::SDL_WINDOW_FULLSCREEN as u32;
142        const FULLSCREEN_DESKTOP = bind::SDL_WINDOW_FULLSCREEN_DESKTOP as u32;
143        const OPENGL = bind::SDL_WINDOW_OPENGL as u32;
144        const VULKAN = bind::SDL_WINDOW_VULKAN as u32;
145        const METAL = bind::SDL_WINDOW_METAL as u32;
146        const SHOWN = bind::SDL_WINDOW_SHOWN as u32;
147        const HIDDEN = bind::SDL_WINDOW_HIDDEN as u32;
148        const BORDERLESS = bind::SDL_WINDOW_BORDERLESS as u32;
149        const RESIZABLE = bind::SDL_WINDOW_RESIZABLE as u32;
150        const MINIMIZED = bind::SDL_WINDOW_MINIMIZED as u32;
151        const MAXIMIZED = bind::SDL_WINDOW_MAXIMIZED as u32;
152        const INPUT_GRABBED = bind::SDL_WINDOW_INPUT_GRABBED as u32;
153        const INPUT_FOCUS = bind::SDL_WINDOW_INPUT_FOCUS as u32;
154        const MOUSE_FOCUS = bind::SDL_WINDOW_MOUSE_FOCUS as u32;
155        const FOREIGN = bind::SDL_WINDOW_FOREIGN as u32;
156        const ALLOW_HIGHDPI = bind::SDL_WINDOW_ALLOW_HIGHDPI as u32;
157        const MOUSE_CAPTURE = bind::SDL_WINDOW_MOUSE_CAPTURE as u32;
158    }
159}