1use std::cell::RefCell;
2use std::collections::{HashMap, HashSet};
3use std::rc::Rc;
4
5use glam::Vec2;
6use repame_input::{AimTracker, GamepadState, MouseState, StagedClick, TouchContact};
7use repose_core::input::{Key, KeyEvent, KeyEventType, PhysicalKey, PointerButton};
8use repose_core::runtime::Scheduler;
9
10#[derive(Default)]
11pub struct Staging {
12 pub held: HashSet<PhysicalKey>,
13 pub edges: Vec<PhysicalKey>,
14 pub window_focused: bool,
15 pub capture_armed: bool,
16 pub clicks: Vec<StagedClick>,
17 pub aim: AimTracker,
18 pub mouse_edges: Vec<(PointerButton, bool)>,
19 pub lmb_held: bool,
20 pub rmb_held: bool,
21 pub rmb_down_edge: bool,
22 pub capture_pending_physical: Option<PhysicalKey>,
23 pub capture_pending_key: Option<Key>,
24 pub capture_pending_mouse: Option<bool>,
25 pub touch_active: HashMap<u64, (Vec2, Vec2)>,
26 pub touch_new: HashSet<u64>,
27 pub pads: Vec<GamepadState>,
28 pub pad_live: bool,
29 pub view_width: f32,
30}
31
32impl Staging {
33 pub fn shared() -> Rc<RefCell<Self>> {
34 Rc::new(RefCell::new(Self {
35 window_focused: true,
36 view_width: 1280.0,
37 ..Default::default()
38 }))
39 }
40
41 pub fn handle_key(&mut self, ke: &KeyEvent) {
42 let down = matches!(ke.event_type, KeyEventType::Down);
43 if down && !ke.is_repeat {
44 match &ke.key {
45 Key::Backspace => {
46 self.capture_pending_key = Some(Key::Backspace);
47 return;
48 }
49 _ => {}
50 }
51 if self.capture_armed {
52 if let Some(key) = ke.physical {
53 self.capture_pending_physical = Some(key);
54 } else {
55 self.capture_pending_key = Some(ke.key.clone());
56 }
57 return;
58 }
59 if matches!(ke.key, Key::Escape | Key::Enter) {
60 return;
61 }
62 }
63 match &ke.key {
64 Key::Escape | Key::Enter => {}
65 Key::Character(_) => {
66 if let Some(key) = ke.physical {
67 self.stage_physical(key, down, ke.is_repeat);
68 }
69 }
70 Key::ShiftLeft => self.stage_code(PhysicalKey::ShiftLeft, down, ke.is_repeat),
71 Key::ShiftRight => self.stage_code(PhysicalKey::ShiftRight, down, ke.is_repeat),
72 Key::Space => self.stage_code(PhysicalKey::Space, down, ke.is_repeat),
73 Key::Tab => self.stage_code(PhysicalKey::Tab, down, ke.is_repeat),
74 Key::ArrowUp => self.stage_code(PhysicalKey::ArrowUp, down, ke.is_repeat),
75 Key::ArrowDown => self.stage_code(PhysicalKey::ArrowDown, down, ke.is_repeat),
76 Key::ArrowLeft => self.stage_code(PhysicalKey::ArrowLeft, down, ke.is_repeat),
77 Key::ArrowRight => self.stage_code(PhysicalKey::ArrowRight, down, ke.is_repeat),
78 _ => {}
79 }
80 }
81
82 pub fn stage_physical(&mut self, key: PhysicalKey, down: bool, is_repeat: bool) {
83 if down {
84 if self.held.insert(key) && !is_repeat {
85 self.edges.push(key);
86 }
87 } else {
88 self.held.remove(&key);
89 }
90 }
91
92 fn stage_code(&mut self, code: PhysicalKey, down: bool, is_repeat: bool) {
93 self.stage_physical(code, down, is_repeat);
94 }
95
96 pub fn stage_physical_key(&mut self, key: PhysicalKey, down: bool) {
97 self.stage_physical(key, down, false);
98 }
99
100 pub fn set_window_focused(&mut self, focused: bool) {
101 self.window_focused = focused;
102 if !focused {
103 self.held.clear();
104 self.edges.clear();
105 self.capture_pending_physical = None;
106 self.capture_pending_key = None;
107 self.capture_pending_mouse = None;
108 self.lmb_held = false;
109 self.rmb_held = false;
110 self.aim = AimTracker::default();
111 self.clicks.clear();
112 self.mouse_edges.clear();
113 self.rmb_down_edge = false;
114 self.touch_active.clear();
115 self.touch_new.clear();
116 self.pads.clear();
117 self.pad_live = false;
118 }
119 }
120
121 pub fn feed_polled(&mut self, sched: &Scheduler) {
122 if !sched.window_focused {
123 self.set_window_focused(false);
124 return;
125 }
126 self.window_focused = true;
127 self.held.retain(|key| sched.held_keys.contains(key));
130 if !sched.mouse_primary {
131 self.lmb_held = false;
132 }
133 if !sched.mouse_secondary {
134 self.rmb_held = false;
135 }
136 if !sched.mouse_middle {
137 self.mouse_edges
138 .retain(|(button, _)| *button != PointerButton::Tertiary);
139 }
140 if sched.touch_points.is_empty() {
144 if !self.touch_active.is_empty() {
145 self.touch_active.clear();
146 self.touch_new.clear();
147 }
148 } else {
149 let d = repose_core::locals::effective_density_scale().max(1e-6);
150 let live: std::collections::HashSet<u64> =
151 sched.touch_points.iter().map(|(id, _, _)| *id).collect();
152 self.touch_active
153 .retain(|id, _| live.contains(id) || self.touch_new.contains(id));
154 for (id, x, y) in sched.touch_points.iter() {
155 let p = Vec2::new(*x, *y) / d;
156 if let Some(contact) = self.touch_active.get_mut(id) {
157 contact.1 = p;
158 } else {
159 self.touch_active.insert(*id, (p, p));
160 self.touch_new.insert(*id);
161 }
162 }
163 }
164 }
165
166 pub fn pick_down(&mut self, button: PointerButton) {
167 if self.capture_armed {
168 self.capture_pending_mouse = Some(button == PointerButton::Primary);
169 return;
170 }
171 self.mouse_edges.push((button, true));
172 if button == PointerButton::Primary {
173 self.lmb_held = true;
174 } else {
175 self.rmb_down_edge = true;
176 self.rmb_held = true;
177 }
178 }
179
180 pub fn pick_up(&mut self, button: PointerButton) {
181 self.mouse_edges.push((button, false));
182 if button == PointerButton::Primary {
183 self.lmb_held = false;
184 } else {
185 self.rmb_held = false;
186 }
187 }
188
189 pub fn take_capture_physical(&mut self) -> Option<PhysicalKey> {
190 self.capture_pending_physical.take()
191 }
192
193 pub fn take_capture_key(&mut self) -> Option<Key> {
194 self.capture_pending_key.take()
195 }
196
197 pub fn take_capture_mouse(&mut self) -> Option<bool> {
198 self.capture_pending_mouse.take()
199 }
200
201 pub fn take_rmb_down(&mut self) -> bool {
202 std::mem::replace(&mut self.rmb_down_edge, false)
203 }
204
205 pub fn stage_click(&mut self, world: Vec2, screen: [f32; 2], density: f32) {
206 let d = density.max(1e-6);
207 self.clicks.push(StagedClick {
208 world,
209 dp: [screen[0] / d, screen[1] / d],
210 });
211 }
212
213 pub fn take_clicks(&mut self) -> Vec<StagedClick> {
214 std::mem::take(&mut self.clicks)
215 }
216
217 pub fn take_edges(&mut self) -> Vec<PhysicalKey> {
218 std::mem::take(&mut self.edges)
219 }
220
221 pub fn take_mouse_edges(&mut self) -> Vec<(PointerButton, bool)> {
222 std::mem::take(&mut self.mouse_edges)
223 }
224
225 pub fn take_pads(&mut self) -> Vec<GamepadState> {
226 self.pad_live = !self.pads.is_empty();
227 std::mem::take(&mut self.pads)
228 }
229
230 pub fn stage_gamepad(&mut self, pad: GamepadState) {
231 self.pads.push(pad);
232 self.pad_live = true;
233 }
234
235 pub fn touch_down(&mut self, id: u64, screen_dp: Vec2) {
236 self.touch_active.insert(id, (screen_dp, screen_dp));
237 self.touch_new.insert(id);
238 }
239
240 pub fn touch_move(&mut self, id: u64, screen_dp: Vec2) {
241 if let Some(contact) = self.touch_active.get_mut(&id) {
242 contact.1 = screen_dp;
243 }
244 }
245
246 pub fn touch_up(&mut self, id: u64) {
247 self.touch_active.remove(&id);
248 self.touch_new.remove(&id);
249 }
250
251 pub fn touch_contacts(&mut self) -> Vec<TouchContact> {
252 let out: Vec<TouchContact> = self
253 .touch_active
254 .iter()
255 .map(|(id, (start, pos))| TouchContact {
256 id: *id,
257 start: *start,
258 pos: *pos,
259 just_pressed: self.touch_new.contains(id),
260 })
261 .collect();
262 self.touch_new.clear();
263 out
264 }
265
266 pub fn cursor_move(&mut self, phys_px: Vec2) {
267 self.aim.cursor_move(phys_px);
268 }
269
270 pub fn stage_hover(&mut self, world: Vec2, screen: [f32; 2]) {
271 self.aim.stage_hover(world, screen);
272 }
273
274 pub fn mouse_state(&self, allow_fire: bool) -> MouseState {
275 MouseState {
276 left_held: self.lmb_held && allow_fire,
277 left_pressed: !self.clicks.is_empty() && allow_fire,
278 right_held: self.rmb_held,
279 right_pressed: self.rmb_down_edge,
280 }
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use repose_core::input::{Key, Modifiers};
288
289 fn key(physical: PhysicalKey) -> KeyEvent {
290 KeyEvent {
291 key: Key::Character('x'),
292 modifiers: Modifiers::default(),
293 is_repeat: false,
294 event_type: KeyEventType::Down,
295 utf16_code_point: 0,
296 physical: Some(physical),
297 }
298 }
299
300 #[test]
301 fn edges_dedupe_while_held() {
302 let mut s = Staging::default();
303 s.handle_key(&key(PhysicalKey::KeyW));
304 s.handle_key(&key(PhysicalKey::KeyW));
305 assert_eq!(s.take_edges().len(), 1);
306 }
307
308 #[test]
309 fn focus_loss_clears_levels() {
310 let mut s = Staging::default();
311 s.handle_key(&key(PhysicalKey::KeyW));
312 s.set_window_focused(false);
313 assert!(s.held.is_empty());
314 }
315
316 #[test]
317 fn capture_stashes_physical_and_skips_edges() {
318 let mut s = Staging::default();
319 s.capture_armed = true;
320 s.handle_key(&key(PhysicalKey::KeyZ));
321 assert_eq!(s.capture_pending_physical, Some(PhysicalKey::KeyZ));
322 assert!(s.take_edges().is_empty());
323 }
324
325 #[test]
326 fn capture_click_stashes_button_not_click_edge() {
327 let mut s = Staging::default();
328 s.capture_armed = true;
329 s.pick_down(PointerButton::Primary);
330 assert_eq!(s.capture_pending_mouse, Some(true));
331 assert!(s.take_clicks().is_empty());
332 assert!(!s.lmb_held);
333 }
334}