1use std::{fs, sync::{Arc, Mutex}};
2
3use fennel_core::{events::{KeyboardEvent, WindowEventHandler}, graphics::WindowConfig, Window};
4use serde::{Deserialize, Serialize};
5use specs::{Dispatcher, DispatcherBuilder, WorldExt};
6
7use crate::{ecs::{input::InputSystem, sprite::{HostPtr, RenderingSystem, Sprite}}, events::KeyEvents};
8
9pub struct App {
12 pub window: fennel_core::Window,
14 pub world: specs::World,
16 pub dispatcher: Dispatcher<'static, 'static>,
18}
19
20#[derive(Default, Debug)]
22pub struct AppBuilder {
23 name: &'static str,
24 dimensions: (u32, u32),
25 config: &'static str,
26 window_config: WindowConfig
27}
28
29#[derive(Deserialize, Serialize, Debug)]
30struct Config {
31 assets_path: String
32}
33
34unsafe impl Send for App {}
35unsafe impl Sync for App {}
36
37#[async_trait::async_trait]
38impl WindowEventHandler for App {
39 fn update(&self, _window: &mut Window) -> anyhow::Result<()> {
40 Ok(())
41 }
42
43 fn draw(&mut self, window: &mut Window) -> anyhow::Result<()> {
44 self.frame_tick();
45 window.graphics.canvas.present();
46 Ok(())
47 }
48
49 fn key_down_event(&self, _window: &mut Window, event: KeyboardEvent) -> anyhow::Result<()> {
50 println!("{:?}", event.keycode);
51 Ok(())
52 }
53}
54
55impl App {
56 pub async fn run(mut self) -> anyhow::Result<()> {
58 let ptr: *mut App = &mut self as *mut App;
64 fennel_core::events::run(&mut self.window, unsafe { &mut *ptr as &mut App }).await;
65 Ok(())
66 }
67
68 pub fn frame_tick(&mut self) {
70 let host_ptr = HostPtr(self as *mut App);
71 self.world.insert(host_ptr);
72 self.dispatcher.dispatch(&self.world);
73 self.world.maintain();
74 self.world.remove::<HostPtr>();
75 }
76}
77
78impl AppBuilder {
79 pub fn new() -> AppBuilder {
81 AppBuilder {
82 name: "",
83 dimensions: (100, 100),
84 config: "",
85 window_config: WindowConfig {
86 resizable: false,
87 fullscreen: false,
88 centered: false
89 }
90 }
91 }
92
93 pub fn name(mut self, name: &'static str) -> AppBuilder {
95 self.name = name;
96 self
97 }
98
99 pub fn dimensions(mut self, dimensions: (u32, u32)) -> AppBuilder {
101 self.dimensions = dimensions;
102 self
103 }
104
105 pub fn config(mut self, path: &'static str) -> AppBuilder {
107 self.config = path;
108 self
109 }
110
111 pub fn build(self) -> anyhow::Result<App> {
113 let resource_manager = Arc::new(Mutex::new(fennel_core::resources::ResourceManager::new()));
114 let config_reader = fs::read(self.config)?;
115 let config: Config = toml::from_slice(&config_reader)?;
116 let graphics = fennel_core::graphics::Graphics::new(
117 self.name.to_string(),
118 self.dimensions,
119 resource_manager.clone(),
120 |graphics| {
121 resource_manager.lock().unwrap().load_dir(config.assets_path.clone().into(), graphics).unwrap();
122 },
123 self.window_config
124 );
125 let window = fennel_core::Window::new(
126 graphics.expect("failed to initialize graphics"),
127 resource_manager,
128 );
129 let mut world = specs::World::new();
130 let mut dispatcher = DispatcherBuilder::new()
131 .with_thread_local(RenderingSystem)
132 .with(InputSystem, "input_system", &[])
133 .build();
134 world.register::<Sprite>();
135 world.insert(KeyEvents::default());
136
137 dispatcher.setup(&mut world);
138
139 Ok(App {
140 window,
141 world,
142 dispatcher,
143 })
144 }
145}