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