Skip to main content

goud_engine/libs/platform/
mod.rs

1//! Platform abstraction layer.
2//!
3//! This module provides the [`PlatformBackend`] trait for windowing and input,
4//! enabling different platform implementations (GLFW, winit, SDL2) to be
5//! swapped without changing higher-level engine code.
6//!
7//! # Architecture
8//!
9//! ```text
10//! ┌──────────────────────────────┐
11//! │   PlatformBackend (trait)    │
12//! ├──────────────────────────────┤
13//! │ GlfwPlatform  │ WinitPlatform│  ← concrete implementations
14//! │  (desktop)    │  (web+desk)  │
15//! └──────────────────────────────┘
16//! ```
17//!
18//! # Usage
19//!
20//! ```rust,ignore
21//! use goud_engine::libs::platform::{PlatformBackend, WindowConfig};
22//! use goud_engine::libs::platform::glfw_platform::GlfwPlatform;
23//!
24//! let config = WindowConfig {
25//!     width: 800,
26//!     height: 600,
27//!     title: "My Game".to_string(),
28//!     ..Default::default()
29//! };
30//! let mut platform = GlfwPlatform::new(&config)?;
31//! ```
32
33#[cfg(feature = "native")]
34pub mod glfw_platform;
35#[cfg(all(feature = "wgpu-backend", feature = "native"))]
36pub mod winit_platform;
37
38#[cfg(feature = "native")]
39use crate::core::input_manager::InputManager;
40
41/// Configuration for creating a platform window.
42#[derive(Debug, Clone)]
43pub struct WindowConfig {
44    /// Window width in pixels.
45    pub width: u32,
46
47    /// Window height in pixels.
48    pub height: u32,
49
50    /// Window title displayed in the title bar.
51    pub title: String,
52
53    /// Enable vertical sync to prevent screen tearing.
54    pub vsync: bool,
55
56    /// Allow the user to resize the window.
57    pub resizable: bool,
58}
59
60impl Default for WindowConfig {
61    fn default() -> Self {
62        Self {
63            width: 800,
64            height: 600,
65            title: "GoudEngine".to_string(),
66            vsync: true,
67            resizable: true,
68        }
69    }
70}
71
72/// Platform backend abstraction for window and input management.
73///
74/// Implementations handle platform-specific window lifecycle, event polling,
75/// input dispatch, and buffer presentation. This trait enables the engine to
76/// support multiple windowing systems through a unified interface.
77///
78/// # Lifecycle
79///
80/// 1. Create the backend via its constructor (e.g., `GlfwPlatform::new(config)`)
81/// 2. Each frame: call [`poll_events`](PlatformBackend::poll_events) → render → call [`swap_buffers`](PlatformBackend::swap_buffers)
82/// 3. Check [`should_close`](PlatformBackend::should_close) to determine when to exit
83///
84/// # Thread Safety
85///
86/// Most windowing APIs require main-thread access. Implementations are NOT
87/// required to be `Send` or `Sync`.
88#[cfg(feature = "native")]
89pub trait PlatformBackend {
90    /// Returns `true` if the window has been requested to close.
91    fn should_close(&self) -> bool;
92
93    /// Sets whether the window should close.
94    fn set_should_close(&mut self, should_close: bool);
95
96    /// Polls platform events and feeds input events to the [`InputManager`].
97    ///
98    /// This advances the input state for the new frame, processes all pending
99    /// platform events, and calculates the time elapsed since the last call.
100    ///
101    /// Returns delta time in seconds since the last call.
102    fn poll_events(&mut self, input: &mut InputManager) -> f32;
103
104    /// Presents the rendered frame by swapping front and back buffers.
105    fn swap_buffers(&mut self);
106
107    /// Returns the logical window size `(width, height)` in screen coordinates.
108    fn get_size(&self) -> (u32, u32);
109
110    /// Returns the physical framebuffer size `(width, height)` in pixels.
111    ///
112    /// This may differ from [`get_size`](PlatformBackend::get_size) on
113    /// HiDPI/Retina displays where the framebuffer resolution is higher
114    /// than the logical window size.
115    fn get_framebuffer_size(&self) -> (u32, u32);
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn window_config_default_values() {
124        let config = WindowConfig::default();
125        assert_eq!(config.width, 800);
126        assert_eq!(config.height, 600);
127        assert_eq!(config.title, "GoudEngine");
128        assert!(config.vsync);
129        assert!(config.resizable);
130    }
131
132    #[test]
133    fn window_config_clone() {
134        let config = WindowConfig {
135            width: 1920,
136            height: 1080,
137            title: "Test".to_string(),
138            vsync: false,
139            resizable: false,
140        };
141        let cloned = config.clone();
142        assert_eq!(config.width, cloned.width);
143        assert_eq!(config.height, cloned.height);
144        assert_eq!(config.title, cloned.title);
145        assert_eq!(config.vsync, cloned.vsync);
146        assert_eq!(config.resizable, cloned.resizable);
147    }
148}