1use std::collections::HashSet;
11use web_time::Duration;
12
13use anyhow::Result;
14pub use repame_sim::{Sim, SimTime};
15pub use repame_sprite::{Camera2d, FrameInput, PickEvent, SpriteInstance};
16pub use repose_core::input::{GamepadEvent, GamepadId};
17use repose_platform::gamepad::{GamepadBackend, create_backend};
18
19pub trait ShellHooks {
22 fn frame_input(&mut self, sim: &Sim) -> FrameInput;
24 fn on_frame(&mut self, _sim: &mut Sim, _dt: Duration) {}
27}
28
29#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
34pub fn run_desktop(
35 title: &str,
36 size: (u32, u32),
37 root: impl FnMut(
38 &mut repose_core::runtime::Scheduler,
39 &repose_core::RenderContext,
40 ) -> repose_core::View
41 + 'static,
42) -> Result<()> {
43 let config = repose_app::AppConfig {
44 window_title: title.to_string(),
45 window_size: size,
46 ..Default::default()
47 };
48 repose_platform::run_desktop_app_with_config(root, config)?;
49 Ok(())
50}
51
52#[cfg(target_arch = "wasm32")]
55pub fn run_web(
56 root: impl FnMut(
57 &mut repose_core::runtime::Scheduler,
58 &repose_core::RenderContext,
59 ) -> repose_core::View
60 + 'static,
61) -> Result<(), wasm_bindgen::JsValue> {
62 let mut options = repose_platform::web::WebOptions::new(None);
63 options.set_prevent_default(true);
64 repose_platform::web::run_web_app(root, options)
65}
66
67#[cfg(target_os = "android")]
70pub fn run_android(
71 app: winit::platform::android::activity::AndroidApp,
72 root: impl FnMut(
73 &mut repose_core::runtime::Scheduler,
74 &repose_core::RenderContext,
75 ) -> repose_core::View
76 + 'static,
77) -> Result<()> {
78 repose_platform::android::run_android_app_with_options(
79 app,
80 root,
81 repose_app::AndroidOptions::default(),
82 )
83}
84
85pub struct GamepadPoller {
90 backend: Option<Box<dyn GamepadBackend>>,
91 connected: HashSet<u32>,
92}
93
94impl GamepadPoller {
95 pub fn new() -> Self {
96 Self {
97 backend: create_backend().map(|b| Box::new(b) as Box<dyn GamepadBackend>),
98 connected: HashSet::new(),
102 }
103 }
104
105 pub fn poll(&mut self) -> Vec<GamepadEvent> {
112 let Some(backend) = &mut self.backend else {
113 return Vec::new();
114 };
115 let events = backend.poll();
116 for ev in &events {
117 match ev {
118 GamepadEvent::Connected { id, .. } => {
119 self.connected.insert(id.0);
120 }
121 GamepadEvent::Disconnected { id } => {
122 self.connected.remove(&id.0);
123 }
124 GamepadEvent::Button { id, .. } | GamepadEvent::Axis { id, .. } => {
125 self.connected.insert(id.0);
126 }
127 }
128 }
129 events
130 }
131
132 pub fn connected_ids(&self) -> Vec<GamepadId> {
133 let mut ids: Vec<GamepadId> = self.connected.iter().copied().map(GamepadId).collect();
134 ids.sort_by_key(|id| id.0);
135 ids
136 }
137
138 pub fn connected_count(&self) -> usize {
139 self.connected.len()
140 }
141}
142
143impl Default for GamepadPoller {
144 fn default() -> Self {
145 Self::new()
146 }
147}