1use std::collections::HashSet;
5use web_time::Duration;
6
7use anyhow::Result;
8pub use repame_sim::{Sim, SimTime};
9pub use repame_sprite::{Camera2d, FrameInput, PickEvent, SpriteInstance};
10pub use repose_core::input::{GamepadEvent, GamepadId};
11use repose_platform::gamepad::{GamepadBackend, create_backend};
12
13mod audio;
14mod levels;
15mod pads;
16mod shortcuts;
17mod staging;
18mod store;
19pub use audio::drain_cues;
20pub use levels::apply_scheduler_levels;
21pub use pads::{PadBank, PadBridge, TRIGGER_HELD};
22pub use shortcuts::{SharedEdges, ShortcutEdges, game_shortcut_map, install_handler_into, install_into, install_map, shared_edges, take as take_shortcut_edges};
23pub use staging::Staging;
24pub use store::{load_json, save_file_path, store_json};
25
26pub trait ShellHooks {
28 fn frame_input(&mut self, sim: &Sim) -> FrameInput;
30 fn on_frame(&mut self, _sim: &mut Sim, _dt: Duration) {}
32}
33
34#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
41pub fn run_desktop(
42 title: &str,
43 size: (u32, u32),
44 root: impl FnMut(
45 &mut repose_core::runtime::Scheduler,
46 &repose_core::RenderContext,
47 ) -> repose_core::View
48 + 'static,
49) -> Result<()> {
50 let config = repose_app::AppConfig {
51 window_title: title.to_string(),
52 window_size: size,
53 ..Default::default()
54 };
55 repose_platform::run_desktop_app_with_config(root, config)?;
56 Ok(())
57}
58
59#[cfg(target_arch = "wasm32")]
61pub fn run_web(
62 root: impl FnMut(
63 &mut repose_core::runtime::Scheduler,
64 &repose_core::RenderContext,
65 ) -> repose_core::View
66 + 'static,
67) -> Result<(), wasm_bindgen::JsValue> {
68 let mut options = repose_platform::web::WebOptions::new(None);
69 options.set_prevent_default(true);
70 repose_platform::web::run_web_app(root, options)
71}
72
73#[cfg(target_os = "android")]
75pub fn run_android(
76 app: winit::platform::android::activity::AndroidApp,
77 root: impl FnMut(
78 &mut repose_core::runtime::Scheduler,
79 &repose_core::RenderContext,
80 ) -> repose_core::View
81 + 'static,
82) -> Result<()> {
83 repose_platform::android::run_android_app_with_options(
84 app,
85 root,
86 repose_app::AndroidOptions::default(),
87 )
88}
89
90pub struct GamepadPoller {
93 backend: Option<Box<dyn GamepadBackend>>,
94 connected: HashSet<u32>,
95}
96
97impl GamepadPoller {
98 pub fn new() -> Self {
99 Self {
100 backend: create_backend().map(|b| Box::new(b) as Box<dyn GamepadBackend>),
101 connected: HashSet::new(),
103 }
104 }
105
106 pub fn poll(&mut self) -> Vec<GamepadEvent> {
109 let Some(backend) = &mut self.backend else {
110 return Vec::new();
111 };
112 let events = backend.poll();
113 for ev in &events {
114 match ev {
115 GamepadEvent::Connected { id, .. } => {
116 self.connected.insert(id.0);
117 }
118 GamepadEvent::Disconnected { id } => {
119 self.connected.remove(&id.0);
120 }
121 GamepadEvent::Button { id, .. } | GamepadEvent::Axis { id, .. } => {
122 self.connected.insert(id.0);
123 }
124 }
125 }
126 events
127 }
128
129 pub fn connected_ids(&self) -> Vec<GamepadId> {
130 let mut ids: Vec<GamepadId> = self.connected.iter().copied().map(GamepadId).collect();
131 ids.sort_by_key(|id| id.0);
132 ids
133 }
134
135 pub fn connected_count(&self) -> usize {
136 self.connected.len()
137 }
138}
139
140impl Default for GamepadPoller {
141 fn default() -> Self {
142 Self::new()
143 }
144}