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::{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 };
41 Self {
42 middleware,
43 internal,
44 settings,
45 last,
46 }
47 }
48}
49
50impl<M> SoftBackend<M>
51where
52 for<'init, 'context, 'surface> M: Middleware<
53 SoftInit<'init>,
54 SoftContext<'context>,
55 SoftSurface<'surface>,
56 SoftEvent,
57 SoftEventContext,
58 (),
59 >,
60{
61 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 let _ = window.request_inner_size(self.settings.render_window_size);
84
85 let context = softbuffer::Context::new(Rc::clone(&window))?;
86 let surface = softbuffer::Surface::new(&context, Rc::clone(&window))?;
87 let size = window.inner_size();
88
89 window.set_visible(true);
90
91 let mut internal = Internal {
92 window,
93 surface,
94 surface_size: size,
95 };
96 let _ = internal.on_resize(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 / 60.0),
124 ));
125 }
126}
127
128impl<M> ApplicationHandler for SoftBackend<M>
129where
130 for<'init, 'control, 'surface> M: Middleware<
131 SoftInit<'init>,
132 SoftContext<'control>,
133 SoftSurface<'surface>,
134 SoftEvent,
135 SoftEventContext,
136 (),
137 >,
138{
139 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
140 if self.internal.is_none() && self.init(event_loop).is_err() {
141 event_loop.exit();
142 }
143 }
144
145 fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
146 if let StartCause::ResumeTimeReached { .. } = cause {
147 self.handle_update(event_loop);
148 }
149 }
150
151 fn window_event(
152 &mut self,
153 event_loop: &ActiveEventLoop,
154 _window_id: WindowId,
155 event: WindowEvent,
156 ) {
157 if let Some(internal) = &mut self.internal {
158 let context = SoftEventContext::new(
159 internal.surface_size,
160 self.settings.scale_mode,
161 self.settings.render_window_size,
162 );
163
164 if let Some(event) = self.middleware.on_event(event, &context, &mut ()) {
165 match event {
166 WindowEvent::Resized(physical_size) => {
167 if let Some(internal) = &mut self.internal {
168 internal.on_resize(physical_size);
169 }
170 }
171 WindowEvent::CloseRequested => {
172 event_loop.exit();
173 }
174 WindowEvent::Destroyed => {
175 event_loop.exit();
176 }
177 WindowEvent::RedrawRequested => {
178 if let Ok(buffer) = internal.surface.buffer_mut() {
179 let mut surface = SoftSurface::new(
180 buffer,
181 internal.surface_size,
182 self.settings.scale_mode,
183 self.settings.render_window_size,
184 );
185 let _ = surface.clear(self.settings.border_color);
186 self.middleware.on_render(&mut surface);
187 let _ = surface.present();
188 }
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
231pub struct SoftInit<'a> {
233 window: &'a Window,
234 settings: &'a mut Settings,
235}
236
237impl SoftInit<'_> {
238 pub fn window(&self) -> &Window {
240 self.window
241 }
242
243 pub fn set_scale(&mut self, scale: u32) {
246 self.settings.set_scale(scale);
247 }
248
249 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 pub fn set_border_color(&mut self, color: u32) {
256 self.settings.border_color = color;
257 }
258}
259
260pub struct SoftContext<'a> {
262 shutdown: bool,
263 window: &'a Window,
264 delta: Duration,
265}
266
267impl SoftContext<'_> {
268 pub fn window(&self) -> &Window {
270 self.window
271 }
272
273 pub fn shutdown(&mut self) {
275 self.shutdown = true;
276 }
277
278 pub fn delta(&self) -> Duration {
280 self.delta
281 }
282}
283
284type SoftEvent = WindowEvent;
285
286pub struct SoftEventContext {
288 render_window: (PhysicalPosition<u32>, PhysicalSize<u32>),
289 scale: u32,
290}
291
292impl SoftEventContext {
293 fn new(
294 surface_size: PhysicalSize<u32>,
295 scale: ScaleMode,
296 render_window_size: PhysicalSize<u32>,
297 ) -> Self {
298 let (render_window_position, scale) =
299 surface::estimate_render_window_position_scale(surface_size, scale, render_window_size);
300 let render_window = (render_window_position, render_window_size);
301 Self {
302 render_window,
303 scale,
304 }
305 }
306}
307
308impl EventContext<PhysicalPosition<f64>> for SoftEventContext {
309 type SurfaceSpace = Option<PhysicalPosition<u32>>;
310
311 fn estimate_surface_space(&self, event_space: PhysicalPosition<f64>) -> Self::SurfaceSpace {
312 let PhysicalPosition { x, y } = event_space;
313 let (x, y) = (x as u32, y as u32);
314 if x > self.render_window.0.x && y > self.render_window.0.y {
315 let (x, y) = (
316 (x - self.render_window.0.x) / self.scale,
317 (y - self.render_window.0.y) / self.scale,
318 );
319 if x < self.render_window.1.width && y < self.render_window.1.height {
320 return Some(PhysicalPosition::new(x, y));
321 }
322 }
323 None
324 }
325}
326
327#[derive(Debug)]
329pub enum Error {
330 WinitEventLoopError(EventLoopError),
332 OsError(OsError),
334 SoftBufferError(softbuffer::SoftBufferError),
336}
337
338impl From<EventLoopError> for Error {
339 fn from(value: EventLoopError) -> Self {
340 Self::WinitEventLoopError(value)
341 }
342}
343
344impl From<OsError> for Error {
345 fn from(value: OsError) -> Self {
346 Self::OsError(value)
347 }
348}
349
350impl From<softbuffer::SoftBufferError> for Error {
351 fn from(value: softbuffer::SoftBufferError) -> Self {
352 Self::SoftBufferError(value)
353 }
354}