1use std::time::Instant;
6
7use lambda_platform::winit::{
8 winit_exports::{
9 ElementState,
10 Event as WinitEvent,
11 MouseButton,
12 WindowEvent as WinitWindowEvent,
13 },
14 Loop,
15 LoopBuilder,
16};
17use logging;
18
19use crate::{
20 component::Component,
21 events::{
22 Button,
23 ComponentEvent,
24 Events,
25 Key,
26 Mouse,
27 RuntimeEvent,
28 WindowEvent,
29 },
30 render::{
31 window::{
32 Window,
33 WindowBuilder,
34 },
35 RenderContext,
36 RenderContextBuilder,
37 },
38 runtime::Runtime,
39};
40
41#[derive(Clone, Debug)]
42pub enum ComponentResult {
43 Success,
44 Failure,
45}
46
47pub struct ApplicationRuntimeBuilder {
48 app_name: String,
49 render_context_builder: RenderContextBuilder,
50 window_builder: WindowBuilder,
51 components: Vec<Box<dyn Component<ComponentResult, String>>>,
52}
53
54impl ApplicationRuntimeBuilder {
55 pub fn new(app_name: &str) -> Self {
56 return Self {
57 app_name: app_name.to_string(),
58 render_context_builder: RenderContextBuilder::new(app_name),
59 window_builder: WindowBuilder::new(),
60 components: Vec::new(),
61 };
62 }
63
64 pub fn with_app_name(mut self, name: &str) -> Self {
66 self.app_name = name.to_string();
67 return self;
68 }
69
70 pub fn with_renderer_configured_as(
75 mut self,
76 configuration: impl FnOnce(RenderContextBuilder) -> RenderContextBuilder,
77 ) -> Self {
78 self.render_context_builder = configuration(self.render_context_builder);
79 return self;
80 }
81
82 pub fn with_window_configured_as(
86 mut self,
87 configuration: impl FnOnce(WindowBuilder) -> WindowBuilder,
88 ) -> Self {
89 self.window_builder = configuration(self.window_builder);
90 return self;
91 }
92
93 pub fn with_component<
95 T: Default + Component<ComponentResult, String> + 'static,
96 >(
97 self,
98 configure_component: impl FnOnce(Self, T) -> (Self, T),
99 ) -> Self {
100 let (mut kernel_builder, component) =
101 configure_component(self, T::default());
102 kernel_builder.components.push(Box::new(component));
103 return kernel_builder;
104 }
105
106 pub fn build(self) -> ApplicationRuntime {
110 let name = self.app_name;
111 let mut event_loop = LoopBuilder::new().build();
112 let window = self.window_builder.build(&mut event_loop);
113
114 let component_stack = self.components;
115 let render_context = self.render_context_builder.build(&window);
116
117 return ApplicationRuntime {
118 name,
119 event_loop,
120 window,
121 render_context,
122 component_stack,
123 };
124 }
125}
126
127pub struct ApplicationRuntime {
130 name: String,
131 event_loop: Loop<Events>,
132 window: Window,
133 component_stack: Vec<Box<dyn Component<ComponentResult, String>>>,
134 render_context: RenderContext,
135}
136
137impl ApplicationRuntime {}
138
139impl Runtime<(), String> for ApplicationRuntime {
140 type Component = Box<dyn Component<ComponentResult, String>>;
141 fn run(self) -> Result<(), String> {
145 let ApplicationRuntime {
148 window,
149 mut event_loop,
150 mut component_stack,
151 name,
152 render_context,
153 } = self;
154
155 let mut active_render_context = Some(render_context);
156
157 let publisher = event_loop.create_event_publisher();
158 publisher.publish_event(Events::Runtime {
159 event: RuntimeEvent::Initialized,
160 issued_at: Instant::now(),
161 });
162
163 let mut current_frame = Instant::now();
164 let mut runtime_result: Box<Result<(), String>> = Box::new(Ok(()));
165
166 event_loop.run_forever(move |event, _, control_flow| {
167 let mapped_event: Option<Events> = match event {
168 WinitEvent::WindowEvent { event, .. } => match event {
169 WinitWindowEvent::CloseRequested => {
170 control_flow.set_exit();
172 Some(Events::Runtime {
173 event: RuntimeEvent::Shutdown,
174 issued_at: Instant::now(),
175 })
176 }
177 WinitWindowEvent::Resized(dims) => {
178 active_render_context
179 .as_mut()
180 .unwrap()
181 .resize(dims.width, dims.height);
182
183 Some(Events::Window {
184 event: WindowEvent::Resize {
185 width: dims.width,
186 height: dims.height,
187 },
188 issued_at: Instant::now(),
189 })
190 }
191 WinitWindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
192 active_render_context
193 .as_mut()
194 .unwrap()
195 .resize(new_inner_size.width, new_inner_size.height);
196
197 Some(Events::Window {
198 event: WindowEvent::Resize {
199 width: new_inner_size.width,
200 height: new_inner_size.height,
201 },
202 issued_at: Instant::now(),
203 })
204 }
205 WinitWindowEvent::Moved(_) => None,
206 WinitWindowEvent::Destroyed => None,
207 WinitWindowEvent::DroppedFile(_) => None,
208 WinitWindowEvent::HoveredFile(_) => None,
209 WinitWindowEvent::HoveredFileCancelled => None,
210 WinitWindowEvent::ReceivedCharacter(_) => None,
211 WinitWindowEvent::Focused(_) => None,
212 WinitWindowEvent::KeyboardInput {
213 device_id: _,
214 input,
215 is_synthetic,
216 } => match (input.state, is_synthetic) {
217 (ElementState::Pressed, false) => Some(Events::Keyboard {
218 event: Key::Pressed {
219 scan_code: input.scancode,
220 virtual_key: input.virtual_keycode,
221 },
222 issued_at: Instant::now(),
223 }),
224 (ElementState::Released, false) => Some(Events::Keyboard {
225 event: Key::Released {
226 scan_code: input.scancode,
227 virtual_key: input.virtual_keycode,
228 },
229 issued_at: Instant::now(),
230 }),
231 _ => {
232 logging::warn!("Unhandled synthetic keyboard event: {:?}", input);
233 None
234 }
235 },
236 WinitWindowEvent::ModifiersChanged(_) => None,
237 WinitWindowEvent::CursorMoved {
238 device_id,
239 position,
240 modifiers,
241 } => Some(Events::Mouse {
242 event: Mouse::Moved {
243 x: position.x,
244 y: position.y,
245 dx: 0.0,
246 dy: 0.0,
247 device_id: 0,
248 },
249 issued_at: Instant::now(),
250 }),
251 WinitWindowEvent::CursorEntered { device_id } => {
252 Some(Events::Mouse {
253 event: Mouse::EnteredWindow { device_id: 0 },
254 issued_at: Instant::now(),
255 })
256 }
257 WinitWindowEvent::CursorLeft { device_id } => Some(Events::Mouse {
258 event: Mouse::LeftWindow { device_id: 0 },
259 issued_at: Instant::now(),
260 }),
261 WinitWindowEvent::MouseWheel {
262 device_id,
263 delta,
264 phase,
265 modifiers,
266 } => Some(Events::Mouse {
267 event: Mouse::Scrolled { device_id: 0 },
268 issued_at: Instant::now(),
269 }),
270 WinitWindowEvent::MouseInput {
271 device_id,
272 state,
273 button,
274 modifiers,
275 } => {
276 let button = match button {
278 MouseButton::Left => Button::Left,
279 MouseButton::Right => Button::Right,
280 MouseButton::Middle => Button::Middle,
281 MouseButton::Other(other) => Button::Other(other),
282 };
283
284 let event = match state {
285 ElementState::Pressed => Mouse::Pressed {
286 button,
287 x: 0.0,
288 y: 0.0,
289 device_id: 0,
290 },
291 ElementState::Released => Mouse::Released {
292 button,
293 x: 0.0,
294 y: 0.0,
295 device_id: 0,
296 },
297 };
298
299 Some(Events::Mouse {
300 event,
301 issued_at: Instant::now(),
302 })
303 }
304 WinitWindowEvent::TouchpadPressure {
305 device_id,
306 pressure,
307 stage,
308 } => None,
309 WinitWindowEvent::AxisMotion {
310 device_id,
311 axis,
312 value,
313 } => None,
314 WinitWindowEvent::Touch(_) => None,
315 WinitWindowEvent::ThemeChanged(_) => None,
316 _ => None,
317 },
318 WinitEvent::MainEventsCleared => {
319 let last_frame = current_frame.clone();
320 current_frame = Instant::now();
321 let duration = ¤t_frame.duration_since(last_frame);
322
323 let active_render_context = active_render_context
324 .as_mut()
325 .expect("Couldn't get the active render context. ");
326 for component in &mut component_stack {
327 component.on_update(duration);
328 let commands = component.on_render(active_render_context);
329 active_render_context.render(commands);
330 }
331
332 match duration.as_millis() > 32 {
334 true => {
335 logging::warn!(
336 "Frame took too long to render: {:?} ms",
337 duration.as_millis()
338 );
339 }
340 false => {
341 }
344 }
345
346 None
347 }
348 WinitEvent::RedrawRequested(_) => None,
349 WinitEvent::NewEvents(_) => None,
350 WinitEvent::DeviceEvent { device_id, event } => None,
351 WinitEvent::UserEvent(lambda_event) => match lambda_event {
352 Events::Runtime { event, issued_at } => match event {
353 RuntimeEvent::Initialized => {
354 logging::debug!(
355 "Initializing all of the components for the runtime: {}",
356 name
357 );
358 for component in &mut component_stack {
359 component.on_attach(active_render_context.as_mut().unwrap());
360 }
361 None
362 }
363 RuntimeEvent::Shutdown => {
364 for component in &mut component_stack {
365 component.on_detach(active_render_context.as_mut().unwrap());
366 }
367 *runtime_result = Ok(());
368 None
369 }
370 RuntimeEvent::ComponentPanic { message } => {
371 *runtime_result = Err(message);
372 None
373 }
374 },
375 _ => None,
376 },
377 WinitEvent::Suspended => None,
378 WinitEvent::Resumed => None,
379 WinitEvent::RedrawEventsCleared => None,
380 WinitEvent::LoopDestroyed => {
381 active_render_context
382 .take()
383 .expect("[ERROR] The render API has been already taken.")
384 .destroy();
385
386 logging::info!("All resources were successfully deleted.");
387 None
388 }
389 };
390
391 match mapped_event {
392 Some(event) => {
393 logging::trace!("Sending event: {:?} to all components", event);
394
395 for component in &mut component_stack {
396 let event_result = component.on_event(event.clone());
397 match event_result {
398 Ok(_) => {}
399 Err(e) => {
400 let error = format!(
401 "A component has panicked while handling an event. {:?}",
402 e
403 );
404 logging::error!(
405 "A component has panicked while handling an event. {:?}",
406 e
407 );
408 publisher.publish_event(Events::Runtime {
409 event: RuntimeEvent::ComponentPanic { message: error },
410 issued_at: Instant::now(),
411 });
412 }
413 }
414 }
415 }
416 None => {}
417 }
418 });
419 return Ok(());
420 }
421
422 fn on_start(&mut self) {
425 logging::info!("Starting the runtime: {}", self.name);
426 }
427
428 fn on_stop(&mut self) {
429 logging::info!("Stopping the runtime: {}", self.name);
430 }
431}