maverick_os/
lib.rs

1use std::collections::BTreeMap;
2use std::future::Future;
3use std::any::TypeId;
4
5#[cfg(target_os = "android")]
6use winit::platform::android::activity::AndroidApp;
7
8mod state;
9pub use state::State;
10
11pub mod hardware;
12use crate::hardware::ApplicationSupport;
13pub use crate::hardware::Cache;
14pub use crate::hardware::PhotoPicker;
15pub use crate::hardware::ImageOrientation;
16pub use crate::hardware::Camera;
17pub use crate::hardware::CameraError;
18
19pub mod runtime;
20use runtime::{Runtime, Services, ThreadConstructor, Service};
21
22pub mod window;
23use window::{WindowManager, EventHandler, Event, Lifetime};
24
25pub mod prelude {
26    pub use crate::{MaverickOS, Application, start};
27}
28
29pub mod air;
30pub use air::Id;
31
32pub trait Application: Services {
33    fn new(context: &mut Context) -> impl Future<Output = Self>;
34    fn on_event(&mut self, context: &mut Context, event: Event) -> impl Future<Output = ()>;
35}
36
37pub struct Context {
38    pub state: Option<State>,
39    pub window: window::Context,
40    pub runtime: runtime::Context,
41    pub hardware: hardware::Context,
42}
43
44//TODO: Need seperate cache for OS level
45//TODO: All cloud access needs to go through the OS
46pub struct MaverickOS<A: Application> {
47    context: Context,
48    services: BTreeMap<TypeId, ThreadConstructor>,
49    app: Option<A>
50}
51
52impl<A: Application + 'static> MaverickOS<A> {
53    pub fn start(
54        #[cfg(target_os = "android")]
55        app: AndroidApp
56    ) {
57        let mut hardware = hardware::Context::new();
58        let runtime = Runtime::start(hardware.clone());
59
60
61        let mut services = BTreeMap::new();
62        let mut background_tasks = BTreeMap::new();
63        let mut pre = A::services().0;
64        while let Some((id, (constructor, backgrounds, deps))) = pre.pop_first() {
65            services.entry(id).or_insert_with(|| {
66                for (id, background) in backgrounds.0 {
67                    background_tasks.insert(id, background);
68                }
69                pre.extend(deps().0);
70                constructor
71            });
72        }
73
74        #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
75        if std::env::args().len() > 1 {
76            runtime.background(&mut hardware, background_tasks.into_values().collect());
77            return
78        }
79
80        WindowManager::start(
81            #[cfg(target_os = "android")]
82            app,
83            MaverickService::<A>::new(runtime, services, hardware)
84        )
85    }
86
87    fn new(services: BTreeMap<TypeId, ThreadConstructor>, context: Context) -> Self {
88        MaverickOS::<A>{context, services, app: None}
89    }
90
91    async fn on_event(&mut self, event: Event) {
92        if self.app.is_none() {
93            self.context.runtime.spawn(air::Service::new(&mut self.context.hardware).await);
94            for service in self.services.values() {
95                self.context.runtime.spawn(service(&mut self.context.hardware).await);
96            }
97            self.app = Some(A::new(&mut self.context).await);
98        }
99        self.app.as_mut().unwrap().on_event(&mut self.context, event).await;
100    }
101}
102
103struct MaverickService<A: Application> {
104    runtime: Option<Runtime>,
105    services: Option<BTreeMap<TypeId, ThreadConstructor>>,
106    hardware: Option<hardware::Context>,
107    os: Option<MaverickOS::<A>>
108}
109impl<A: Application> MaverickService<A> {
110    fn new(runtime: Runtime, services: BTreeMap<TypeId, ThreadConstructor>, hardware: hardware::Context) -> Self {
111        MaverickService{runtime: Some(runtime), services: Some(services), hardware: Some(hardware), os: None}
112    }
113}
114
115impl<A: Application + 'static> EventHandler for MaverickService<A> {
116    fn event(&mut self, window_ctx: &window::Context, event: Event) {
117        if let Some(runtime) = self.runtime.as_mut() {
118            if self.os.is_none() {
119                self.os = Some(MaverickOS::new(self.services.take().unwrap(), Context{
120                    hardware: self.hardware.take().unwrap(),
121                    runtime: runtime.context().clone(),
122                    window: window_ctx.clone(),
123                    state: Some(State::default())
124                }))
125            }
126            self.os.as_mut().map(|a| {
127                runtime.tick(a.context.state.as_mut().unwrap())
128            });
129
130            let os = self.os.as_mut().unwrap();
131            os.context.window = window_ctx.clone();
132            runtime.block_on(os.on_event(event.clone()));
133            match &event {
134                Event::Lifetime(Lifetime::Paused) => runtime.pause(),
135                Event::Lifetime(Lifetime::Resumed) => runtime.resume(),
136                Event::Lifetime(Lifetime::Close) => self.runtime.take().unwrap().close(),
137                _ => {}
138            }
139        }
140    }
141}
142
143#[macro_export]
144macro_rules! start {
145    ($app:ty) => {
146        #[cfg(target_arch = "wasm32")]
147        #[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
148        pub fn maverick_main() {
149            MaverickOS::<$app>::start()
150        }
151
152        #[cfg(target_os = "ios")]
153        #[unsafe(no_mangle)]
154        pub extern "C" fn maverick_main() {
155            MaverickOS::<$app>::start()
156        }
157
158        #[cfg(target_os = "android")]
159        #[no_mangle]
160        pub fn maverick_main(app: AndroidApp) {
161            MaverickOS::<$app>::start(app)
162        }
163
164        #[cfg(not(any(target_os = "android", target_os="ios", target_arch = "wasm32")))]
165        pub fn maverick_main() {
166            MaverickOS::<$app>::start()
167        }
168    };
169}