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;
20pub use winit;
21
22mod surface;
23
24pub struct SoftBackend<M> {
26 middleware: M,
27 internal: Option<Internal>,
28 settings: Settings,
29 last: Instant,
30}
31
32impl<M> SoftBackend<M> {
33 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 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 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
295pub struct SoftInit<'a> {
297 window: &'a Window,
298 settings: &'a mut Settings,
299}
300
301impl SoftInit<'_> {
302 pub fn window(&self) -> &Window {
304 self.window
305 }
306
307 pub fn set_scale(&mut self, scale: u32) {
310 self.settings.set_scale(scale);
311 }
312
313 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 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 pub fn set_border_color(&mut self, color: u32) {
331 self.settings.border_color = color;
332 }
333}
334
335pub struct SoftContext<'a> {
337 shutdown: bool,
338 window: &'a Window,
339 delta: Duration,
340}
341
342impl SoftContext<'_> {
343 pub fn window(&self) -> &Window {
345 self.window
346 }
347
348 pub fn shutdown(&mut self) {
350 self.shutdown = true;
351 }
352
353 pub fn delta(&self) -> Duration {
355 self.delta
356 }
357}
358
359pub enum SoftEvent {
361 Window(WindowEvent),
363 Device(DeviceId, DeviceEvent),
365}
366
367pub 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
408pub struct SoftEventControl<'a> {
410 event_loop: &'a ActiveEventLoop,
411}
412
413impl SoftEventControl<'_> {
414 pub fn shutdown(&self) {
416 self.event_loop.exit();
417 }
418}
419
420#[derive(Debug)]
422pub enum Error {
423 WinitEventLoopError(EventLoopError),
425 OsError(OsError),
427 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}