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