kobalt_runtime/
desktop.rs

1//! Desktop platform implementation using winit
2
3use kobalt_core::types::Size;
4use kobalt_render::Renderer;
5use std::sync::Arc;
6use winit::application::ApplicationHandler;
7use winit::event::{Event, WindowEvent};
8use winit::event_loop::{ActiveEventLoop, EventLoop};
9use winit::window::{Window, WindowAttributes, WindowId};
10
11/// Desktop window wrapper
12pub struct DesktopWindow {
13    window: Arc<Window>,
14    renderer: Option<Renderer>,
15}
16
17impl DesktopWindow {
18    /// Creates a new desktop window
19    async fn new(event_loop: &ActiveEventLoop, title: &str, width: u32, height: u32) -> Self {
20        let attributes = WindowAttributes::default()
21            .with_title(title)
22            .with_inner_size(winit::dpi::PhysicalSize::new(width, height));
23
24        let window = Arc::new(event_loop.create_window(attributes).unwrap());
25
26        // Create WGPU instance and surface
27        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
28            backends: wgpu::Backends::all(),
29            ..Default::default()
30        });
31
32        let surface = instance.create_surface(window.clone()).unwrap();
33
34        let size = Size::new(width as f32, height as f32);
35        let renderer = Renderer::new_with_instance(instance, surface, size).await;
36
37        Self {
38            window,
39            renderer: Some(renderer),
40        }
41    }
42
43    /// Returns the window
44    pub fn window(&self) -> &Window {
45        &self.window
46    }
47
48    /// Returns the renderer
49    pub fn renderer(&self) -> &Renderer {
50        self.renderer.as_ref().unwrap()
51    }
52
53    /// Returns a mutable reference to the renderer
54    pub fn renderer_mut(&mut self) -> &mut Renderer {
55        self.renderer.as_mut().unwrap()
56    }
57
58    /// Resizes the window
59    pub fn resize(&mut self, width: u32, height: u32) {
60        if let Some(renderer) = &mut self.renderer {
61            renderer.resize(Size::new(width as f32, height as f32));
62        }
63    }
64}
65
66/// Desktop application
67pub struct DesktopApp<F>
68where
69    F: FnMut(&mut DesktopWindow, &Event<()>) + 'static,
70{
71    window: Option<DesktopWindow>,
72    on_event: F,
73    window_config: WindowConfig,
74}
75
76struct WindowConfig {
77    title: String,
78    width: u32,
79    height: u32,
80}
81
82impl<F> DesktopApp<F>
83where
84    F: FnMut(&mut DesktopWindow, &Event<()>) + 'static,
85{
86    /// Creates a new desktop application
87    pub fn new(title: &str, width: u32, height: u32, on_event: F) -> Self {
88        Self {
89            window: None,
90            on_event,
91            window_config: WindowConfig {
92                title: title.to_string(),
93                width,
94                height,
95            },
96        }
97    }
98
99    /// Runs the application
100    pub fn run(mut self) -> Result<(), Box<dyn std::error::Error>> {
101        let event_loop = EventLoop::new()?;
102        event_loop.run_app(&mut self)?;
103        Ok(())
104    }
105}
106
107impl<F> ApplicationHandler for DesktopApp<F>
108where
109    F: FnMut(&mut DesktopWindow, &Event<()>) + 'static,
110{
111    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
112        if self.window.is_none() {
113            let window = pollster::block_on(DesktopWindow::new(
114                event_loop,
115                &self.window_config.title,
116                self.window_config.width,
117                self.window_config.height,
118            ));
119            self.window = Some(window);
120        }
121    }
122
123    fn window_event(
124        &mut self,
125        event_loop: &ActiveEventLoop,
126        window_id: WindowId,
127        event: WindowEvent,
128    ) {
129        if let Some(window) = &mut self.window {
130            if window.window.id() == window_id {
131                match event {
132                    WindowEvent::CloseRequested => {
133                        event_loop.exit();
134                    }
135                    WindowEvent::Resized(physical_size) => {
136                        window.resize(physical_size.width, physical_size.height);
137                    }
138                    WindowEvent::RedrawRequested => {
139                        // Trigger custom event handler
140                        (self.on_event)(
141                            window,
142                            &Event::WindowEvent {
143                                window_id,
144                                event: WindowEvent::RedrawRequested,
145                            },
146                        );
147                        window.window.request_redraw();
148                    }
149                    _ => {}
150                }
151            }
152        }
153    }
154
155    fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
156        if let Some(window) = &self.window {
157            window.window.request_redraw();
158        }
159    }
160}