1use crate::runner::ApiState;
4use nightshade::prelude::*;
5use nightshade::run::pump::{PumpShell, pump_frame, pump_shell_new, pump_shell_ready};
6
7pub struct Window {
9 pub title: String,
10 pub size: Option<(u32, u32)>,
11}
12
13impl Default for Window {
14 fn default() -> Self {
15 Self {
16 title: "nightshade".to_string(),
17 size: None,
18 }
19 }
20}
21
22pub struct App {
29 pub world: World,
30 pub frame_limit: Option<u32>,
31 frames_rendered: u32,
32 shell: PumpShell,
33}
34
35pub fn open() -> App {
39 open_with(Window::default())
40}
41
42pub fn open_with(window: Window) -> App {
44 let state = ApiState::<()> {
45 setup: None,
46 update: None,
47 data: None,
48 clears_draw_pools: false,
49 frame_limit: None,
50 frames_rendered: 0,
51 };
52 let mut shell =
53 pump_shell_new(Box::new(state)).expect("failed to create the engine event loop");
54 shell.context.world.resources.window.title = window.title;
55
56 let mut pumps_without_window = 0;
57 while !pump_shell_ready(&shell) {
58 if !pump_frame(&mut shell) {
59 break;
60 }
61 let window_exists = shell.context.world.resources.window.handle.is_some();
62 if window_exists && shell.context.initialized && shell.context.renderer.is_none() {
63 panic!("failed to create the renderer, see the log for the error");
64 }
65 if !window_exists {
66 pumps_without_window += 1;
67 if pumps_without_window > 10000 {
68 panic!("failed to create the window, see the log for the error");
69 }
70 }
71 }
72
73 if let Some((width, height)) = window.size
74 && let Some(handle) = shell.context.world.resources.window.handle.clone()
75 && let Some(size) = handle.request_inner_size(winit::dpi::PhysicalSize::new(width, height))
76 && size.width > 0
77 && size.height > 0
78 {
79 shell.context.world.resources.window.cached_viewport_size = Some((size.width, size.height));
80 if let Some(renderer) = shell.context.renderer.as_mut()
81 && let Err(error) = renderer.resize_surface(size.width, size.height)
82 {
83 tracing::error!("Failed to resize surface: {error}");
84 }
85 }
86
87 let mut world = World::default();
88 std::mem::swap(&mut world, &mut shell.context.world);
89
90 schedule_remove(
91 &mut world.resources.schedules.frame,
92 system_names::RESET_MOUSE,
93 );
94 schedule_remove(
95 &mut world.resources.schedules.frame,
96 system_names::RESET_KEYBOARD,
97 );
98 schedule_remove(
99 &mut world.resources.schedules.frame,
100 system_names::RESET_TOUCH,
101 );
102
103 let frame_limit = crate::runner::frame_limit_from_environment();
104
105 App {
106 world,
107 frame_limit,
108 frames_rendered: 0,
109 shell,
110 }
111}
112
113pub fn frame(app: &mut App) -> bool {
125 if let Some(limit) = app.frame_limit
126 && app.frames_rendered >= limit
127 {
128 return false;
129 }
130
131 nightshade::ecs::input::systems::reset_mouse_system(&mut app.world);
132 nightshade::ecs::input::systems::reset_keyboard_system(&mut app.world);
133 nightshade::ecs::input::systems::reset_touch_system(&mut app.world);
134
135 std::mem::swap(&mut app.world, &mut app.shell.context.world);
136 let alive = pump_frame(&mut app.shell);
137 std::mem::swap(&mut app.world, &mut app.shell.context.world);
138
139 crate::draw::clear_draw_pools(&mut app.world);
140 app.frames_rendered += 1;
141
142 alive
143}