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::{Controls, Devices, WheelRate};
55
56use core::num::NonZeroU32;
57
58use crate::math::Vec2;
59use state::Ticks;
60use table::{Rebound, Table};
61
62pub(crate) struct Queries {
66 table: Table,
67 controls: Controls,
68 ticks: Ticks,
69 dirty: bool,
70}
71
72impl Queries {
73 pub(crate) fn new<A: InputActions>(kept: Option<&str>) -> Self {
77 Self {
78 table: Table::new::<A>(kept),
79 controls: Controls::default(),
80 ticks: Ticks::default(),
81 dirty: false,
82 }
83 }
84
85 pub(crate) fn take(&mut self, controls: Controls) {
88 self.ticks.fold(&controls);
89 self.controls = controls;
90 }
91
92 pub(crate) fn ticks(&mut self, count: NonZeroU32, mut tick: impl FnMut(&Ticking<'_>)) {
97 let ticking = Ticking { queries: self };
98 for _ in 0..count.get() {
99 tick(&ticking);
100 }
101 }
102
103 pub(crate) fn down<A: InputButtonAction>(&self, action: A) -> bool {
104 self.controls.frame().down(self.table.resolve(action))
105 }
106
107 pub(crate) fn pressed<A: InputButtonAction>(&self, action: A) -> bool {
108 self.controls.frame().pressed(self.table.resolve(action))
109 }
110
111 pub(crate) fn released<A: InputButtonAction>(&self, action: A) -> bool {
112 self.controls.frame().released(self.table.resolve(action))
113 }
114
115 pub(crate) fn clicks<A: InputButtonAction>(&self, action: A) -> u32 {
116 match self.pressed(action) {
117 true => self.controls.clicks(self.table.resolve(action)),
118 false => 0,
119 }
120 }
121
122 pub(crate) fn axis<A: InputAxisAction>(&self, action: A) -> f32 {
123 self.controls.frame().axis(self.table.resolve(action))
124 }
125
126 pub(crate) fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2 {
127 self.controls.frame().axis2(self.table.resolve(action))
128 }
129
130 pub(crate) fn pointer(&self) -> Vec2 {
131 self.controls.frame().pointer()
132 }
133
134 pub(crate) fn bindings<A: InputAction>(&self, action: A) -> Vec<A::Binding> {
135 self.table.resolve(action).to_vec()
136 }
137
138 pub(crate) fn rebind<A: InputAction>(&mut self, action: A, bindings: Vec<A::Binding>) {
141 match self.table.rebind(action, bindings) {
142 Rebound::Changed => self.dirty = true,
143 Rebound::Unchanged => {}
144 }
145 }
146
147 pub(crate) fn flush(&mut self) -> Option<String> {
151 core::mem::take(&mut self.dirty).then(|| self.table.written())
152 }
153
154 pub(crate) fn actuated_button(&self) -> Option<ButtonBinding> {
155 self.controls.frame().actuated_button()
156 }
157
158 pub(crate) fn actuated_axis(&self) -> Option<AxisBinding> {
159 self.controls.frame().actuated_axis()
160 }
161
162 pub(crate) fn actuated_axis2(&self) -> Option<Axis2Binding> {
163 self.controls.frame().actuated_axis2()
164 }
165
166 fn pressed_over_ticks<A: InputButtonAction>(&self, action: A) -> bool {
169 self.ticks.snapshot().pressed(self.table.resolve(action))
170 }
171
172 fn released_over_ticks<A: InputButtonAction>(&self, action: A) -> bool {
175 self.ticks.snapshot().released(self.table.resolve(action))
176 }
177
178 fn clicks_over_ticks<A: InputButtonAction>(&self, action: A) -> u32 {
181 match self.pressed_over_ticks(action) {
182 true => self.controls.clicks(self.table.resolve(action)),
183 false => 0,
184 }
185 }
186
187 fn ticked(&mut self) {
190 self.ticks.ticked();
191 }
192}
193
194pub(crate) struct Ticking<'a> {
200 queries: &'a mut Queries,
201}
202
203impl Ticking<'_> {
204 pub(crate) fn down<A: InputButtonAction>(&self, action: A) -> bool {
205 self.queries.down(action)
206 }
207
208 pub(crate) fn pressed<A: InputButtonAction>(&self, action: A) -> bool {
209 self.queries.pressed_over_ticks(action)
210 }
211
212 pub(crate) fn released<A: InputButtonAction>(&self, action: A) -> bool {
213 self.queries.released_over_ticks(action)
214 }
215
216 pub(crate) fn clicks<A: InputButtonAction>(&self, action: A) -> u32 {
217 self.queries.clicks_over_ticks(action)
218 }
219
220 pub(crate) fn axis<A: InputAxisAction>(&self, action: A) -> f32 {
221 self.queries.axis(action)
222 }
223
224 pub(crate) fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2 {
225 self.queries.axis2(action)
226 }
227
228 pub(crate) fn pointer(&self) -> Vec2 {
229 self.queries.pointer()
230 }
231}
232
233impl Drop for Ticking<'_> {
234 fn drop(&mut self) {
235 self.queries.ticked();
236 }
237}
238
239mod action;
240mod binding;
241mod cursor;
242mod pad;
243mod state;
244mod table;
245
246#[cfg(test)]
247mod tests {
248 use core::time::Duration;
249
250 use super::*;
251 use winit::event::{DeviceId, ElementState, WindowEvent};
252
253 const DOUBLE_CLICK: Duration = Duration::from_millis(400);
255
256 const TICKS: NonZeroU32 = match NonZeroU32::new(3) {
259 Some(ticks) => ticks,
260 None => NonZeroU32::MIN,
261 };
262
263 fn run() -> (Devices, Queries) {
267 (
268 Devices::new(Pads::silent(), DOUBLE_CLICK),
269 Queries::new::<Key>(None),
270 )
271 }
272
273 fn click() -> WindowEvent {
277 WindowEvent::MouseInput {
278 device_id: DeviceId::dummy(),
279 state: ElementState::Pressed,
280 button: winit::event::MouseButton::Left,
281 }
282 }
283
284 fn pressed_per_tick(queries: &mut Queries, count: NonZeroU32, action: Key) -> Vec<bool> {
286 let mut read = Vec::new();
287 queries.ticks(count, |ticking| read.push(ticking.pressed(action)));
288 read
289 }
290
291 #[test]
292 fn only_a_rebind_that_changed_something_leaves_the_store_to_write() {
293 let (_, mut queries) = run();
294
295 queries.rebind(Key::Space, vec![Key::Space.into()]);
296 assert!(!queries.dirty, "what it read through already is no change");
297
298 queries.rebind(Key::Space, vec![Key::Enter.into()]);
299 assert!(queries.dirty);
300 assert!(
301 queries.flush().is_some(),
302 "and the text to write is handed out"
303 );
304
305 assert!(
306 queries.flush().is_none(),
307 "so the store is written once, not per call"
308 );
309 assert_eq!(queries.bindings(Key::Space), vec![Key::Enter.into()]);
310 }
311
312 #[test]
313 fn the_queries_answer_from_the_controls_alone_and_count_clicks_across_them() {
314 let (mut devices, mut queries) = run();
315 queries.rebind(Key::Space, vec![MouseButton::Left.into()]);
316 devices.see(&click());
317 queries.take(devices.sample(Duration::ZERO));
318 let first = (queries.pressed(Key::Space), queries.clicks(Key::Space));
319
320 devices.press(MouseButton::Left.into(), false);
321 devices.sample(Duration::ZERO);
322 devices.press(MouseButton::Left.into(), true);
323 queries.take(devices.sample(Duration::from_millis(100)));
324
325 assert_eq!(
326 (queries.pressed(Key::Space), queries.clicks(Key::Space)),
327 (first.0, first.1 + 1),
328 "the same press read through the controls, one click further on"
329 );
330 assert!(
331 queries.pressed_over_ticks(Key::Space),
332 "and the ticks' edge crosses in the same controls"
333 );
334 }
335
336 #[test]
337 fn the_controls_are_plain_data_that_cross_between_threads() {
338 fn crosses<T: Send + Clone>() {}
339 crosses::<Controls>();
340 }
341
342 #[test]
343 fn the_ticks_of_one_frame_read_an_edge_and_the_ticks_after_them_read_none() {
344 let (mut devices, mut queries) = run();
345 queries.rebind(Key::Space, vec![MouseButton::Left.into()]);
346
347 devices.see(&click());
348 queries.take(devices.sample(Duration::ZERO));
349 queries.take(devices.sample(Duration::ZERO));
350
351 assert_eq!(
352 pressed_per_tick(&mut queries, TICKS, Key::Space),
353 [true; 3],
354 "every tick of the frame that reads the press reads it"
355 );
356
357 queries.take(devices.sample(Duration::ZERO));
358 assert_eq!(
359 pressed_per_tick(&mut queries, TICKS, Key::Space),
360 [false; 3],
361 "and the ticks that follow read it no more"
362 );
363 }
364}