khora_infra/platform/window/
winit.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A `winit`-based implementation of the `KhoraWindow` trait.
16
17use khora_core::platform::window::{KhoraWindow, KhoraWindowHandle};
18use raw_window_handle::{
19    DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
20};
21use std::sync::Arc;
22use winit::{dpi::LogicalSize, error::OsError, event_loop::ActiveEventLoop, window::Window};
23
24/// A wrapper around a `winit::window::Window` that implements the `KhoraWindow` trait.
25///
26/// This struct provides a concrete window implementation for desktop platforms,
27/// abstracting the engine's core logic from the specifics of the `winit` crate.
28/// It uses an `Arc` internally to allow for cheap cloning and shared ownership.
29#[derive(Debug, Clone)]
30pub struct WinitWindow {
31    inner: Arc<Window>,
32}
33
34/// A builder for creating `WinitWindow` instances.
35///
36/// This follows the builder pattern to provide an ergonomic API for window creation.
37pub struct WinitWindowBuilder {
38    title: String,
39    width: u32,
40    height: u32,
41}
42
43impl WinitWindowBuilder {
44    /// Creates a new `WinitWindowBuilder` with default settings.
45    pub fn new() -> Self {
46        Self {
47            title: "Khora Engine".to_string(),
48            width: 1024,
49            height: 768,
50        }
51    }
52
53    /// Sets the title of the window to be built.
54    pub fn with_title(mut self, title: impl Into<String>) -> Self {
55        self.title = title.into();
56        self
57    }
58
59    /// Sets the initial inner dimensions of the window to be built.
60    pub fn with_dimensions(mut self, width: u32, height: u32) -> Self {
61        self.width = width;
62        self.height = height;
63        self
64    }
65
66    /// Builds the `WinitWindow` using the provided `winit` event loop.
67    ///
68    /// # Errors
69    /// Returns an `OsError` if the underlying `winit` window creation fails.
70    pub fn build(self, event_loop: &ActiveEventLoop) -> Result<WinitWindow, OsError> {
71        log::info!(
72            "Building window with title: '{}' and size: {}x{}",
73            self.title,
74            self.width,
75            self.height
76        );
77
78        let window_attributes = Window::default_attributes()
79            .with_title(self.title)
80            .with_inner_size(LogicalSize::new(self.width, self.height))
81            .with_visible(true);
82
83        let window = event_loop.create_window(window_attributes)?;
84
85        log::info!("Winit window created successfully (id: {:?}).", window.id());
86        Ok(WinitWindow {
87            inner: Arc::new(window),
88        })
89    }
90}
91
92impl Default for WinitWindowBuilder {
93    /// Creates a new `WinitWindowBuilder` with default settings.
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl HasWindowHandle for WinitWindow {
100    /// Provides the raw window handle required by graphics backends.
101    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
102        self.inner.window_handle()
103    }
104}
105
106impl HasDisplayHandle for WinitWindow {
107    /// Provides the raw display handle required by graphics backends.
108    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
109        self.inner.display_handle()
110    }
111}
112
113impl KhoraWindow for WinitWindow {
114    /// Returns the physical dimensions (width, height) of the window's inner area.
115    fn inner_size(&self) -> (u32, u32) {
116        let size = self.inner.inner_size();
117        (size.width, size.height)
118    }
119
120    /// Returns the display's scale factor, used for HiDPI rendering.
121    fn scale_factor(&self) -> f64 {
122        self.inner.scale_factor()
123    }
124
125    /// Requests that the window be redrawn.
126    fn request_redraw(&self) {
127        self.inner.request_redraw();
128    }
129
130    /// Clones a thread-safe, reference-counted handle to the window.
131    fn clone_handle_arc(&self) -> KhoraWindowHandle {
132        self.inner.clone()
133    }
134
135    /// Returns a stable, unique identifier for the window.
136    fn id(&self) -> u64 {
137        use std::collections::hash_map::DefaultHasher;
138        use std::hash::{Hash, Hasher};
139
140        let mut hasher = DefaultHasher::new();
141        self.inner.id().hash(&mut hasher);
142        hasher.finish()
143    }
144}