devotee_backend_softbuffer/
lib.rs1#![deny(missing_docs)]
2
3use 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
23pub struct SoftBackend<M> {
25 middleware: M,
26 internal: Option<Internal>,
27 settings: Settings,
28 last: Instant,
29}
30
31impl<M> SoftBackend<M> {
32 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 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 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
294pub struct SoftInit<'a> {
296 window: &'a Window,
297 settings: &'a mut Settings,
298}
299
300impl SoftInit<'_> {
301 pub fn window(&self) -> &Window {
303 self.window
304 }
305
306 pub fn set_scale(&mut self, scale: u32) {
309 self.settings.set_scale(scale);
310 }
311
312 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 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 pub fn set_border_color(&mut self, color: u32) {
330 self.settings.border_color = color;
331 }
332}
333
334pub struct SoftContext<'a> {
336 shutdown: bool,
337 window: &'a Window,
338 delta: Duration,
339}
340
341impl SoftContext<'_> {
342 pub fn window(&self) -> &Window {
344 self.window
345 }
346
347 pub fn shutdown(&mut self) {
349 self.shutdown = true;
350 }
351
352 pub fn delta(&self) -> Duration {
354 self.delta
355 }
356}
357
358pub enum SoftEvent {
360 Window(WindowEvent),
362 Device(DeviceId, DeviceEvent),
364}
365
366pub 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
407pub struct SoftEventControl<'a> {
409 event_loop: &'a ActiveEventLoop,
410}
411
412impl SoftEventControl<'_> {
413 pub fn shutdown(&self) {
415 self.event_loop.exit();
416 }
417}
418
419#[derive(Debug)]
421pub enum Error {
422 WinitEventLoopError(EventLoopError),
424 OsError(OsError),
426 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}