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}