Skip to main content

astrelis_winit/
window.rs

1use std::sync::Arc;
2
3use astrelis_core::geometry::{LogicalSize, PhysicalSize, ScaleFactor};
4pub use winit::dpi::PhysicalSize as WinitPhysicalSize;
5pub use winit::window::Fullscreen;
6pub use winit::window::Window as WinitWindow;
7use winit::{error::OsError, event_loop::ActiveEventLoop};
8
9pub struct WindowDescriptor {
10    pub title: String,
11    pub resizeable: bool,
12    pub size: Option<winit::dpi::PhysicalSize<f32>>,
13    pub visible: bool,
14    pub fullscreen: Option<Fullscreen>,
15}
16
17impl Default for WindowDescriptor {
18    fn default() -> Self {
19        Self {
20            title: "Astrelis Window".to_string(),
21            resizeable: true,
22            size: None,
23            visible: true,
24            fullscreen: None,
25        }
26    }
27}
28
29pub struct Window {
30    pub window: Arc<winit::window::Window>,
31}
32
33impl Window {
34    pub fn id(&self) -> winit::window::WindowId {
35        self.window.id()
36    }
37
38    /// Get the logical size of the window (DPI-independent).
39    pub fn logical_size(&self) -> LogicalSize<u32> {
40        let physical_size = self.window.inner_size();
41        let scale_factor = self.window.scale_factor();
42        LogicalSize::new(
43            (physical_size.width as f64 / scale_factor) as u32,
44            (physical_size.height as f64 / scale_factor) as u32,
45        )
46    }
47
48    /// Get the physical size of the window in pixels.
49    pub fn physical_size(&self) -> PhysicalSize<u32> {
50        self.window.inner_size().into()
51    }
52
53    /// Get the scale factor for this window.
54    pub fn scale_factor(&self) -> ScaleFactor {
55        ScaleFactor(self.window.scale_factor())
56    }
57
58    /// Get the raw scale factor as f64.
59    pub fn scale_factor_f64(&self) -> f64 {
60        self.window.scale_factor()
61    }
62
63    pub fn platform_dpi() -> f64 {
64        #[cfg(target_os = "macos")]
65        return 2.0;
66        #[cfg(not(target_os = "macos"))]
67        return 1.0;
68    }
69
70    pub(crate) fn new(
71        event_loop: &ActiveEventLoop,
72        descriptor: WindowDescriptor,
73    ) -> Result<Self, OsError> {
74        let mut attributes = WinitWindow::default_attributes()
75            .with_title(descriptor.title)
76            .with_resizable(descriptor.resizeable)
77            .with_visible(descriptor.visible)
78            .with_fullscreen(descriptor.fullscreen);
79
80        if let Some(size) = descriptor.size {
81            attributes = attributes.with_inner_size(size);
82        }
83
84        let window = Arc::new(event_loop.create_window(attributes)?);
85
86        Ok(Window { window })
87    }
88}
89
90pub trait WindowBackend {
91    type FrameContext;
92    type Error;
93
94    /// Begin drawing a new frame.
95    ///
96    /// Returns a frame context that can be used to issue draw commands.
97    /// May fail if the surface is lost or outdated - in that case, the caller
98    /// should handle the error (e.g., by skipping this frame or reconfiguring the surface).
99    fn try_begin_drawing(&mut self) -> Result<Self::FrameContext, Self::Error>;
100
101    /// Begin drawing a new frame, panicking on error.
102    ///
103    /// This is a convenience method that panics if the surface cannot be acquired.
104    /// For production code, prefer `try_begin_drawing()` which allows graceful error handling.
105    fn begin_drawing(&mut self) -> Self::FrameContext
106    where
107        Self::Error: std::fmt::Debug,
108    {
109        self.try_begin_drawing()
110            .expect("Failed to begin drawing - consider using try_begin_drawing() for graceful error handling")
111    }
112}
113
114pub trait WindowExt {
115    /// Requests a redraw of the window.
116    ///
117    /// WindowBackend::begin_drawing should be preferred over this method where possible.
118    /// This method only serves as a fallback when no drawing backend is available.
119    fn request_redraw(&self);
120}
121
122impl WindowExt for Window {
123    fn request_redraw(&self) {
124        self.window.request_redraw();
125    }
126}