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, 'event_context> M: Middleware<
53            SoftInit<'init>,
54            SoftContext<'context>,
55            SoftSurface<'surface>,
56            SoftEvent,
57            SoftEventContext<'event_context>,
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
84        let context = softbuffer::Context::new(Rc::clone(&window))?;
85        let surface = softbuffer::Surface::new(&context, Rc::clone(&window))?;
86        let size = window.inner_size();
87
88        window.set_visible(true);
89
90        let mut internal = Internal {
91            window,
92            surface,
93            surface_size: size,
94        };
95        let _ = internal.on_resize(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 / 60.0),
123        ));
124    }
125}
126
127impl<M> ApplicationHandler for SoftBackend<M>
128where
129    for<'init, 'control, 'surface, 'event_context> M: Middleware<
130            SoftInit<'init>,
131            SoftContext<'control>,
132            SoftSurface<'surface>,
133            SoftEvent,
134            SoftEventContext<'event_context>,
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 surface = SoftSurface::new(
158                &mut internal.surface,
159                internal.surface_size,
160                self.settings.scale_mode,
161                self.settings.render_window_size,
162            );
163
164            let context = SoftEventContext { surface };
165
166            if let Some(event) = self.middleware.on_event(event, &context, &mut ()) {
167                match event {
168                    WindowEvent::Resized(physical_size) => {
169                        if let Some(internal) = &mut self.internal {
170                            internal.on_resize(physical_size);
171                        }
172                    }
173                    WindowEvent::CloseRequested => {
174                        event_loop.exit();
175                    }
176                    WindowEvent::Destroyed => {
177                        event_loop.exit();
178                    }
179                    WindowEvent::RedrawRequested => {
180                        let mut surface = SoftSurface::new(
181                            &mut internal.surface,
182                            internal.surface_size,
183                            self.settings.scale_mode,
184                            self.settings.render_window_size,
185                        );
186                        let _ = surface.clear(self.settings.border_color);
187                        self.middleware.on_render(&mut surface);
188                        let _ = surface.present();
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 ititalization argument passed to the application.
232pub struct SoftInit<'a> {
233    window: &'a Window,
234    settings: &'a mut Settings,
235}
236
237impl SoftInit<'_> {
238    /// Set window title.
239    pub fn set_title(&mut self, title: &str) {
240        self.window.set_title(title);
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<'a> {
288    surface: SoftSurface<'a>,
289}
290
291impl EventContext<PhysicalPosition<f64>> for SoftEventContext<'_> {
292    type SurfaceSpace = Option<PhysicalPosition<u32>>;
293
294    fn estimate_surface_space(&self, event_space: PhysicalPosition<f64>) -> Self::SurfaceSpace {
295        let PhysicalPosition { x, y } = event_space;
296        let (x, y) = (x as u32, y as u32);
297        if x > self.surface.render_window_position().x
298            && y > self.surface.render_window_position().y
299        {
300            let (x, y) = (
301                (x - self.surface.render_window_position().x) / self.surface.render_window_scale(),
302                (y - self.surface.render_window_position().y) / self.surface.render_window_scale(),
303            );
304            if x < self.surface.render_window_size().width
305                && y < self.surface.render_window_size().height
306            {
307                return Some(PhysicalPosition::new(x, y));
308            }
309        }
310        None
311    }
312}
313
314/// An error generalization.
315#[derive(Debug)]
316pub enum Error {
317    /// Winit event loop error.
318    WinitEventLoopError(EventLoopError),
319    /// Os error.
320    OsError(OsError),
321    /// SoftBuffer error.
322    SoftBufferError(softbuffer::SoftBufferError),
323}
324
325impl From<EventLoopError> for Error {
326    fn from(value: EventLoopError) -> Self {
327        Self::WinitEventLoopError(value)
328    }
329}
330
331impl From<OsError> for Error {
332    fn from(value: OsError) -> Self {
333        Self::OsError(value)
334    }
335}
336
337impl From<softbuffer::SoftBufferError> for Error {
338    fn from(value: softbuffer::SoftBufferError) -> Self {
339        Self::SoftBufferError(value)
340    }
341}