devotee_backend_softbuffer/
lib.rs

1#![deny(missing_docs)]
2
3//! [Softbuffer](https://crates.io/crates/softbuffer)-based backend for the devotee project.
4
5use std::rc::Rc;
6use std::time::{Duration, Instant};
7
8use devotee_backend::Middleware;
9use devotee_backend::middling::EventContext;
10use winit::application::ApplicationHandler;
11use winit::dpi::{PhysicalPosition, PhysicalSize};
12use winit::error::{EventLoopError, OsError};
13use winit::event::{StartCause, WindowEvent};
14use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
15use winit::window::{Window, WindowAttributes, WindowId};
16
17use surface::ScaleMode;
18
19pub use surface::SoftSurface;
20
21mod surface;
22
23/// [Softbuffer](https://crates.io/crates/softbuffer)-based backend implementation for the devotee project.
24pub struct SoftBackend<M> {
25    middleware: M,
26    internal: Option<Internal>,
27    settings: Settings,
28    last: Instant,
29}
30
31impl<M> SoftBackend<M> {
32    /// Create new backend instance.
33    pub fn new(middleware: M) -> Self {
34        let internal = None;
35        let last = Instant::now();
36        let settings = Settings {
37            render_window_size: PhysicalSize::new(32, 32),
38            border_color: 0,
39            scale_mode: ScaleMode::Auto,
40            updates_per_seconds: 60.0,
41        };
42        Self {
43            middleware,
44            internal,
45            settings,
46            last,
47        }
48    }
49}
50
51impl<M> SoftBackend<M>
52where
53    for<'init, 'context, 'surface> M: Middleware<
54            SoftInit<'init>,
55            SoftContext<'context>,
56            SoftSurface<'surface>,
57            SoftEvent,
58            SoftEventContext,
59            (),
60        >,
61{
62    /// Run this backend to completion.
63    pub fn run(&mut self) -> Result<(), Error> {
64        let event_loop = EventLoop::new()?;
65
66        event_loop.set_control_flow(ControlFlow::WaitUntil(
67            Instant::now() + Duration::from_secs_f32(1.0 / self.settings.updates_per_seconds),
68        ));
69        event_loop.run_app(self)?;
70
71        Ok(())
72    }
73
74    fn init(&mut self, event_loop: &ActiveEventLoop) -> Result<(), Error> {
75        let window = Rc::new(event_loop.create_window(WindowAttributes::default())?);
76
77        let mut init = SoftInit {
78            window: &window,
79            settings: &mut self.settings,
80        };
81
82        window.set_visible(true);
83        self.middleware.on_init(&mut init);
84        window.set_min_inner_size(Some(self.settings.render_window_size));
85
86        let context = softbuffer::Context::new(Rc::clone(&window))?;
87        let surface = softbuffer::Surface::new(&context, Rc::clone(&window))?;
88        let surface_size = window.inner_size();
89
90        let mut internal = Internal {
91            window,
92            surface,
93            surface_size,
94        };
95        let _ = internal.on_resize(surface_size);
96
97        self.internal = Some(internal);
98
99        Ok(())
100    }
101
102    fn handle_update(&mut self, event_loop: &ActiveEventLoop) {
103        let now = Instant::now();
104        if let Some(internal) = &self.internal {
105            let delta = now - self.last;
106
107            let mut control = SoftContext {
108                shutdown: false,
109                window: &internal.window,
110                delta,
111            };
112            self.middleware.on_update(&mut control);
113
114            internal.window.request_redraw();
115
116            if control.shutdown {
117                event_loop.exit();
118            }
119        }
120        self.last = now;
121        event_loop.set_control_flow(ControlFlow::WaitUntil(
122            now + Duration::from_secs_f32(1.0 / self.settings.updates_per_seconds),
123        ));
124    }
125}
126
127impl<M> ApplicationHandler for SoftBackend<M>
128where
129    for<'init, 'control, 'surface> M: Middleware<
130            SoftInit<'init>,
131            SoftContext<'control>,
132            SoftSurface<'surface>,
133            SoftEvent,
134            SoftEventContext,
135            (),
136        >,
137{
138    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
139        if self.internal.is_none() && self.init(event_loop).is_err() {
140            event_loop.exit();
141        }
142    }
143
144    fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
145        if let StartCause::ResumeTimeReached { .. } = cause {
146            self.handle_update(event_loop);
147        }
148    }
149
150    fn window_event(
151        &mut self,
152        event_loop: &ActiveEventLoop,
153        _window_id: WindowId,
154        event: WindowEvent,
155    ) {
156        if let Some(internal) = &mut self.internal {
157            let context = SoftEventContext::new(
158                internal.surface_size,
159                self.settings.scale_mode,
160                self.settings.render_window_size,
161            );
162
163            if let Some(event) = self.middleware.on_event(event, &context, &mut ()) {
164                match event {
165                    WindowEvent::Resized(physical_size) => {
166                        if let Some(internal) = &mut self.internal {
167                            internal.on_resize(physical_size);
168                        }
169                    }
170                    WindowEvent::CloseRequested => {
171                        event_loop.exit();
172                    }
173                    WindowEvent::Destroyed => {
174                        event_loop.exit();
175                    }
176                    WindowEvent::RedrawRequested => {
177                        if let Ok(buffer) = internal.surface.buffer_mut() {
178                            let mut surface = SoftSurface::new(
179                                buffer,
180                                internal.surface_size,
181                                self.settings.scale_mode,
182                                self.settings.render_window_size,
183                            );
184                            let _ = surface.clear(self.settings.border_color);
185                            self.middleware.on_render(&mut surface);
186                            let _ = surface.present();
187                        }
188                    }
189                    _ => {}
190                }
191            }
192        }
193    }
194}
195
196struct Settings {
197    render_window_size: PhysicalSize<u32>,
198    border_color: u32,
199    scale_mode: ScaleMode,
200    updates_per_seconds: f32,
201}
202
203impl Settings {
204    fn set_scale(&mut self, scale: u32) {
205        if let Ok(scale) = scale.try_into() {
206            self.scale_mode = ScaleMode::Fixed(scale);
207        } else {
208            self.scale_mode = ScaleMode::Auto;
209        }
210    }
211
212    /// # Panics
213    ///
214    /// Panics if `updates_per_second` is less or equal to `0`.
215    fn set_updates_per_second(&mut self, updates_per_second: f32) {
216        assert!(
217            updates_per_second > 0.0,
218            "Update rate has to be greater than 0"
219        );
220        self.updates_per_seconds = updates_per_second;
221    }
222}
223
224struct Internal {
225    window: Rc<Window>,
226    surface: softbuffer::Surface<Rc<Window>, Rc<Window>>,
227    surface_size: PhysicalSize<u32>,
228}
229
230impl Internal {
231    fn on_resize(&mut self, size: PhysicalSize<u32>) -> Option<()> {
232        self.surface
233            .resize(size.width.try_into().ok()?, size.height.try_into().ok()?)
234            .ok()?;
235
236        self.surface_size = size;
237
238        Some(())
239    }
240}
241
242/// An initialization argument passed to the application.
243pub struct SoftInit<'a> {
244    window: &'a Window,
245    settings: &'a mut Settings,
246}
247
248impl SoftInit<'_> {
249    /// Get internal `winit` window reference.
250    pub fn window(&self) -> &Window {
251        self.window
252    }
253
254    /// Set internal render scale.
255    /// If case of `0` `scale` value automatic scaling is used.
256    pub fn set_scale(&mut self, scale: u32) {
257        self.settings.set_scale(scale);
258    }
259
260    /// Set update framerate in updates per second count.
261    ///
262    /// # Panics
263    ///
264    /// Panics if `updates_per_second` is less or equal to `0`.
265    pub fn set_updates_per_second(&mut self, updates_per_second: f32) {
266        self.settings.set_updates_per_second(updates_per_second);
267    }
268
269    /// Set the internal render window size.
270    pub fn set_render_window_size(&mut self, width: u32, height: u32) {
271        let size = PhysicalSize::new(width, height);
272        self.settings.render_window_size = size;
273        let _ = self.window.request_inner_size(size);
274    }
275
276    /// Set the color of the border to be rendered.
277    pub fn set_border_color(&mut self, color: u32) {
278        self.settings.border_color = color;
279    }
280}
281
282/// An update argument passed to the application.
283pub struct SoftContext<'a> {
284    shutdown: bool,
285    window: &'a Window,
286    delta: Duration,
287}
288
289impl SoftContext<'_> {
290    /// Get reference to the underlying `winit` `Window` reference.
291    pub fn window(&self) -> &Window {
292        self.window
293    }
294
295    /// Tell the backend to shut itself down.
296    pub fn shutdown(&mut self) {
297        self.shutdown = true;
298    }
299
300    /// Get delta time estimation.
301    pub fn delta(&self) -> Duration {
302        self.delta
303    }
304}
305
306type SoftEvent = WindowEvent;
307
308/// A context passed to the event handler.
309pub struct SoftEventContext {
310    render_window: (PhysicalPosition<u32>, PhysicalSize<u32>),
311    scale: u32,
312}
313
314impl SoftEventContext {
315    fn new(
316        surface_size: PhysicalSize<u32>,
317        scale: ScaleMode,
318        render_window_size: PhysicalSize<u32>,
319    ) -> Self {
320        let (render_window_position, scale) =
321            surface::estimate_render_window_position_scale(surface_size, scale, render_window_size);
322        let render_window = (render_window_position, render_window_size);
323        Self {
324            render_window,
325            scale,
326        }
327    }
328}
329
330impl EventContext<PhysicalPosition<f64>> for SoftEventContext {
331    type SurfaceSpace = Option<PhysicalPosition<u32>>;
332
333    fn estimate_surface_space(&self, event_space: PhysicalPosition<f64>) -> Self::SurfaceSpace {
334        let PhysicalPosition { x, y } = event_space;
335        let (x, y) = (x as u32, y as u32);
336        if x > self.render_window.0.x && y > self.render_window.0.y {
337            let (x, y) = (
338                (x - self.render_window.0.x) / self.scale,
339                (y - self.render_window.0.y) / self.scale,
340            );
341            if x < self.render_window.1.width && y < self.render_window.1.height {
342                return Some(PhysicalPosition::new(x, y));
343            }
344        }
345        None
346    }
347}
348
349/// An error generalization.
350#[derive(Debug)]
351pub enum Error {
352    /// Winit event loop error.
353    WinitEventLoopError(EventLoopError),
354    /// Os error.
355    OsError(OsError),
356    /// SoftBuffer error.
357    SoftBufferError(softbuffer::SoftBufferError),
358}
359
360impl From<EventLoopError> for Error {
361    fn from(value: EventLoopError) -> Self {
362        Self::WinitEventLoopError(value)
363    }
364}
365
366impl From<OsError> for Error {
367    fn from(value: OsError) -> Self {
368        Self::OsError(value)
369    }
370}
371
372impl From<softbuffer::SoftBufferError> for Error {
373    fn from(value: softbuffer::SoftBufferError) -> Self {
374        Self::SoftBufferError(value)
375    }
376}