1pub use action::{
41 InputAction, InputActions, InputAxis2Action, InputAxisAction, InputButtonAction,
42 NoInputActions, NoInputAxes, NoInputAxes2, NoInputButtons,
43};
44pub use binding::{
45 Axis2Binding, AxisBinding, ButtonAxis, ButtonAxis2, ButtonBinding, JoystickControl, Key,
46 MouseButton, Pad, PadAxis, PointerDelta, Stick, WheelDelta,
47};
48pub use cursor::Cursor;
49
50#[cfg(feature = "offscreen")]
51pub use state::Switch;
52
53pub(crate) use pad::Pads;
54pub(crate) use state::WheelRate;
55
56use core::num::NonZeroU32;
57use core::time::Duration;
58
59use winit::event::{DeviceEvent, WindowEvent};
60
61use crate::math::Vec2;
62use crate::platform::Store;
63use state::Devices;
64use table::{Rebound, Table};
65
66pub(crate) struct Input {
69 table: Table,
70 devices: Devices,
71 pads: Pads,
72 store: Store,
73 dirty: bool,
74 double_click: Duration,
76}
77
78impl Input {
79 pub(crate) fn new<A: InputActions>(pads: Pads, store: Store, double_click: Duration) -> Self {
82 Self {
83 table: Table::new::<A>(store.read().as_deref()),
84 devices: Devices::default(),
85 pads,
86 store,
87 dirty: false,
88 double_click,
89 }
90 }
91
92 pub(crate) fn see(&mut self, event: &WindowEvent) {
93 self.devices.see(event);
94 }
95
96 pub(crate) fn see_device(&mut self, event: &DeviceEvent) {
99 self.devices.see_device(event);
100 }
101
102 pub(crate) fn hold_pointer(&mut self, held: bool) {
105 self.devices.hold_pointer(held);
106 }
107
108 #[cfg(feature = "offscreen")]
111 pub(crate) fn press(&mut self, control: Switch, down: bool) {
112 self.devices.press(control, down);
113 }
114
115 #[cfg(feature = "offscreen")]
118 pub(crate) fn point_at(&mut self, at: Vec2) {
119 self.devices.point_at(at);
120 }
121
122 #[cfg(feature = "offscreen")]
125 pub(crate) fn move_pointer(&mut self, lane: PointerDelta, pixels: f32) {
126 self.devices.move_pointer(lane.moving(pixels));
127 }
128
129 #[cfg(feature = "offscreen")]
132 pub(crate) fn turn_wheel(&mut self, lane: WheelDelta, notches: f32) {
133 self.devices.turn_wheel(lane.turning(notches));
134 }
135
136 #[cfg(all(feature = "ui", feature = "offscreen"))]
144 pub(crate) fn ui_modifiers(&self) -> egui::Modifiers {
145 let either = |left, right| {
146 self.devices.holds(Switch::Key(left)) || self.devices.holds(Switch::Key(right))
147 };
148 let ctrl = either(Key::LeftControl, Key::RightControl);
149
150 egui::Modifiers {
151 alt: either(Key::LeftAlt, Key::RightAlt),
152 ctrl,
153 shift: either(Key::LeftShift, Key::RightShift),
154 mac_cmd: false,
155 command: ctrl,
156 }
157 }
158
159 #[cfg(all(feature = "ui", feature = "offscreen"))]
163 pub(crate) fn pointing_at(&self) -> Option<Vec2> {
164 self.devices.pointing_at()
165 }
166
167 pub(crate) fn sample(&mut self, at: Duration) {
170 self.devices.sample(&mut self.pads, at, self.double_click);
171 }
172
173 pub(crate) fn ticks(&mut self, count: NonZeroU32, mut tick: impl FnMut(&Ticking<'_>)) {
178 let ticking = Ticking { input: self };
179 for _ in 0..count.get() {
180 tick(&ticking);
181 }
182 }
183
184 pub(crate) fn down<A: InputButtonAction>(&self, action: A) -> bool {
185 self.devices.snapshot().down(self.table.resolve(action))
186 }
187
188 pub(crate) fn pressed<A: InputButtonAction>(&self, action: A) -> bool {
189 self.devices.snapshot().pressed(self.table.resolve(action))
190 }
191
192 pub(crate) fn released<A: InputButtonAction>(&self, action: A) -> bool {
193 self.devices.snapshot().released(self.table.resolve(action))
194 }
195
196 pub(crate) fn clicks<A: InputButtonAction>(&self, action: A) -> u32 {
197 match self.pressed(action) {
198 true => self.devices.clicks(self.table.resolve(action)),
199 false => 0,
200 }
201 }
202
203 pub(crate) fn axis<A: InputAxisAction>(&self, action: A) -> f32 {
204 self.devices.snapshot().axis(self.table.resolve(action))
205 }
206
207 pub(crate) fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2 {
208 self.devices.snapshot().axis2(self.table.resolve(action))
209 }
210
211 pub(crate) fn pointer(&self) -> Vec2 {
212 self.devices.snapshot().pointer()
213 }
214
215 pub(crate) fn bindings<A: InputAction>(&self, action: A) -> Vec<A::Binding> {
216 self.table.resolve(action).to_vec()
217 }
218
219 pub(crate) fn rebind<A: InputAction>(&mut self, action: A, bindings: Vec<A::Binding>) {
222 match self.table.rebind(action, bindings) {
223 Rebound::Changed => self.dirty = true,
224 Rebound::Unchanged => {}
225 }
226 }
227
228 pub(crate) fn flush(&mut self) {
231 if core::mem::take(&mut self.dirty) {
232 self.store.write(&self.table.written());
233 }
234 }
235
236 pub(crate) fn actuated_button(&self) -> Option<ButtonBinding> {
237 self.devices.snapshot().actuated_button()
238 }
239
240 pub(crate) fn actuated_axis(&self) -> Option<AxisBinding> {
241 self.devices.snapshot().actuated_axis()
242 }
243
244 pub(crate) fn actuated_axis2(&self) -> Option<Axis2Binding> {
245 self.devices.snapshot().actuated_axis2()
246 }
247
248 fn pressed_over_ticks<A: InputButtonAction>(&self, action: A) -> bool {
251 self.devices.ticks().pressed(self.table.resolve(action))
252 }
253
254 fn released_over_ticks<A: InputButtonAction>(&self, action: A) -> bool {
257 self.devices.ticks().released(self.table.resolve(action))
258 }
259
260 fn clicks_over_ticks<A: InputButtonAction>(&self, action: A) -> u32 {
263 match self.pressed_over_ticks(action) {
264 true => self.devices.clicks(self.table.resolve(action)),
265 false => 0,
266 }
267 }
268
269 fn ticked(&mut self) {
271 self.devices.ticked();
272 }
273}
274
275pub(crate) struct Ticking<'a> {
281 input: &'a mut Input,
282}
283
284impl Ticking<'_> {
285 pub(crate) fn down<A: InputButtonAction>(&self, action: A) -> bool {
286 self.input.down(action)
287 }
288
289 pub(crate) fn pressed<A: InputButtonAction>(&self, action: A) -> bool {
290 self.input.pressed_over_ticks(action)
291 }
292
293 pub(crate) fn released<A: InputButtonAction>(&self, action: A) -> bool {
294 self.input.released_over_ticks(action)
295 }
296
297 pub(crate) fn clicks<A: InputButtonAction>(&self, action: A) -> u32 {
298 self.input.clicks_over_ticks(action)
299 }
300
301 pub(crate) fn axis<A: InputAxisAction>(&self, action: A) -> f32 {
302 self.input.axis(action)
303 }
304
305 pub(crate) fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2 {
306 self.input.axis2(action)
307 }
308
309 pub(crate) fn pointer(&self) -> Vec2 {
310 self.input.pointer()
311 }
312}
313
314impl Drop for Ticking<'_> {
315 fn drop(&mut self) {
316 self.input.ticked();
317 }
318}
319
320mod action;
321mod binding;
322mod cursor;
323mod pad;
324mod state;
325mod table;
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330 use winit::event::{DeviceId, ElementState};
331
332 const DOUBLE_CLICK: Duration = Duration::from_millis(400);
334
335 const TICKS: NonZeroU32 = match NonZeroU32::new(3) {
338 Some(ticks) => ticks,
339 None => NonZeroU32::MIN,
340 };
341
342 fn input() -> Input {
345 Input::new::<Key>(Pads::silent(), Store::bindings(None), DOUBLE_CLICK)
346 }
347
348 fn click() -> WindowEvent {
352 WindowEvent::MouseInput {
353 device_id: DeviceId::dummy(),
354 state: ElementState::Pressed,
355 button: winit::event::MouseButton::Left,
356 }
357 }
358
359 fn pressed_per_tick(input: &mut Input, count: NonZeroU32, action: Key) -> Vec<bool> {
361 let mut read = Vec::new();
362 input.ticks(count, |ticking| read.push(ticking.pressed(action)));
363 read
364 }
365
366 #[test]
367 fn only_a_rebind_that_changed_something_leaves_the_store_to_write() {
368 let mut input = input();
369
370 input.rebind(Key::Space, vec![Key::Space.into()]);
371 assert!(!input.dirty, "what it read through already is no change");
372
373 input.rebind(Key::Space, vec![Key::Enter.into()]);
374 assert!(input.dirty);
375 input.flush();
376
377 assert!(!input.dirty, "so the store is written once, not per call");
378 assert_eq!(input.bindings(Key::Space), vec![Key::Enter.into()]);
379 }
380
381 #[test]
382 fn the_ticks_of_one_frame_read_an_edge_and_the_ticks_after_them_read_none() {
383 let mut input = input();
384 input.rebind(Key::Space, vec![MouseButton::Left.into()]);
385
386 input.see(&click());
387 input.sample(Duration::ZERO);
388 input.sample(Duration::ZERO);
389
390 assert_eq!(
391 pressed_per_tick(&mut input, TICKS, Key::Space),
392 [true; 3],
393 "every tick of the frame that reads the press reads it"
394 );
395
396 input.sample(Duration::ZERO);
397 assert_eq!(
398 pressed_per_tick(&mut input, TICKS, Key::Space),
399 [false; 3],
400 "and the ticks that follow read it no more"
401 );
402 }
403}