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