gilrs/ev/
filter.rs

1// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Alter events in various ways.
9//!
10//! This modules contains "event filters" that can change, drop or create new events. To use them,
11//! import `Filter` trait and call `filter()` function on `Option<Event>`. Because `filter` also
12//! returns `Option<Event>` you can combine multiple filters by using `filter()` function on
13//! returned event.
14//!
15//! Filters in this modules have public fields that can be used to configure their behaviour. You
16//! can also create them with default values using `new()` method. If filter is not configurable,
17//! it is implemented as function (for example `deadzone()`).
18//!
19//! # Example
20//!
21//! ```
22//! use gilrs::{GilrsBuilder, Filter};
23//! use gilrs::ev::filter::{Jitter, Repeat, deadzone};
24//!
25//! let mut gilrs = GilrsBuilder::new().with_default_filters(false).build().unwrap();
26//! let jitter = Jitter { threshold: 0.02 };
27//! let repeat = Repeat::new();
28//!
29//! // Event loop
30//! loop {
31//!     while let Some(event) = gilrs
32//!         .next_event()
33//!         .filter_ev(&jitter, &mut gilrs)
34//!         .filter_ev(&deadzone, &mut gilrs)
35//!         .filter_ev(&repeat, &mut gilrs)
36//!     {
37//!         gilrs.update(&event);
38//!         println!("{:?}", event);
39//!     }
40//!     # break;
41//! }
42//! ```
43//! # Implementing custom filters
44//!
45//! If you want to implement your own filters, you will have to implement `FilterFn` trait.
46//! **Do not return `None` if you got `Some(event)`**. If you want to discard an event, uses
47//! `EventType::Dropped`. Returning `None` means that there are no more events to process and
48//! will end `while let` loop.
49//!
50//! ## Example
51//!
52//! Example implementations of filter that will drop all events with `Unknown` axis or button.
53//!
54//! ```
55//! use gilrs::ev::filter::FilterFn;
56//! use gilrs::{Gilrs, Event, EventType, Button, Axis, Filter};
57//!
58//! struct UnknownSlayer;
59//!
60//! impl FilterFn for UnknownSlayer {
61//!     fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> {
62//!         match ev {
63//!             Some(Event { event: EventType::ButtonPressed(Button::Unknown, ..), id, .. })
64//!             | Some(Event { event: EventType::ButtonReleased(Button::Unknown, ..), id, .. })
65//!             | Some(Event { event: EventType::AxisChanged(Axis::Unknown, ..), id, .. })
66//!             => Some(Event::new(id, EventType::Dropped)),
67//!             _ => ev,
68//!         }
69//!     }
70//! }
71//! ```
72//!
73//! `FilterFn` is also implemented for all `Fn(Option<Event>, &Gilrs) -> Option<Event>`, so above
74//! example could be simplified to passing closure to `filter()` function.
75
76use crate::ev::{Axis, AxisOrBtn, Button, Code, Event, EventType};
77use crate::gamepad::{Gamepad, Gilrs};
78use crate::utils;
79
80use std::time::Duration;
81
82/// Discard axis events that changed less than `threshold`.
83#[derive(Copy, Clone, PartialEq, Debug)]
84pub struct Jitter {
85    pub threshold: f32,
86}
87
88impl Jitter {
89    /// Creates new `Repeat` filter with threshold set to 0.01.
90    pub fn new() -> Self {
91        Jitter { threshold: 0.01 }
92    }
93}
94
95impl Default for Jitter {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101impl FilterFn for Jitter {
102    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
103        match ev {
104            Some(Event {
105                event: EventType::AxisChanged(_, val, axis),
106                id,
107                ..
108            }) => match gilrs.gamepad(id).state().axis_data(axis) {
109                Some(data) if val != 0.0 && (val - data.value()).abs() < self.threshold => {
110                    Some(Event::new(id, EventType::Dropped))
111                }
112                _ => ev,
113            },
114            _ => ev,
115        }
116    }
117}
118
119fn apply_deadzone(x: f32, y: f32, threshold: f32) -> (f32, f32) {
120    let magnitude = utils::clamp((x * x + y * y).sqrt(), 0.0, 1.0);
121    if magnitude <= threshold {
122        (0.0, 0.0)
123    } else {
124        let norm = ((magnitude - threshold) / (1.0 - threshold)) / magnitude;
125        (x * norm, y * norm)
126    }
127}
128
129fn deadzone_nonzero_axis_idx(axis: Axis) -> Option<usize> {
130    Some(match axis {
131        Axis::DPadX => 0,
132        Axis::DPadY => 1,
133        Axis::LeftStickX => 2,
134        Axis::LeftStickY => 3,
135        Axis::RightStickX => 4,
136        Axis::RightStickY => 5,
137        _ => {
138            return None;
139        }
140    })
141}
142
143/// Drops events in dead zone and remaps value to keep it in standard range.
144pub fn deadzone(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
145    match ev {
146        Some(Event {
147            event: EventType::AxisChanged(axis, val, nec),
148            id,
149            time,
150        }) => {
151            let threshold = match gilrs.gamepad(id).deadzone(nec) {
152                Some(t) => t,
153                None => return ev,
154            };
155
156            if let Some((other_axis, other_code)) = axis
157                .second_axis()
158                .and_then(|axis| gilrs.gamepad(id).axis_code(axis).map(|code| (axis, code)))
159            {
160                let other_val = gilrs.gamepad(id).state().value(other_code);
161                let val = apply_deadzone(val, other_val, threshold);
162
163                // Since this is the second axis, deadzone_nonzero_axis_idx() will always returns something.
164                let other_axis_idx = deadzone_nonzero_axis_idx(other_axis).unwrap();
165
166                if val.0 == 0.
167                    && val.1 == 0.
168                    && gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx]
169                    && gilrs.gamepad(id).state().value(other_code) != 0.
170                {
171                    // Clear other axis that is now within the dead zone threshold.
172                    gilrs.insert_event(Event {
173                        id,
174                        time,
175                        event: EventType::AxisChanged(other_axis, 0., other_code),
176                    });
177                    gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx] = false;
178                }
179
180                Some(if gilrs.gamepad(id).state().value(nec) == val.0 {
181                    Event::new(id, EventType::Dropped)
182                } else {
183                    if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {
184                        gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] =
185                            val.0 != 0.;
186                    }
187                    Event {
188                        id,
189                        time,
190                        event: EventType::AxisChanged(axis, val.0, nec),
191                    }
192                })
193            } else {
194                let val = apply_deadzone(val, 0.0, threshold).0;
195
196                Some(if gilrs.gamepad(id).state().value(nec) == val {
197                    Event::new(id, EventType::Dropped)
198                } else {
199                    if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {
200                        gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] = val != 0.;
201                    }
202                    Event {
203                        id,
204                        time,
205                        event: EventType::AxisChanged(axis, val, nec),
206                    }
207                })
208            }
209        }
210        Some(Event {
211            event: EventType::ButtonChanged(btn, val, nec),
212            id,
213            time,
214        }) => {
215            let gp = &gilrs.gamepad(id);
216            let threshold = match gp.deadzone(nec) {
217                Some(t) => t,
218                None => return ev,
219            };
220            let val = apply_deadzone(val, 0.0, threshold).0;
221
222            Some(if gp.state().value(nec) == val {
223                Event::new(id, EventType::Dropped)
224            } else {
225                Event {
226                    id,
227                    time,
228                    event: EventType::ButtonChanged(btn, val, nec),
229                }
230            })
231        }
232        _ => ev,
233    }
234}
235
236/// Maps axis dpad events to button dpad events.
237///
238/// This filter will do nothing if gamepad has dpad buttons (to prevent double events for same
239/// element) and if standard `NativeEvCode` for dpads is used by some other buttons. It will always
240/// try to map if SDL mappings contains mappings for all four hats.
241pub fn axis_dpad_to_button(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
242    use gilrs_core::native_ev_codes as necs;
243
244    fn can_map(gp: &Gamepad<'_>) -> bool {
245        let hats_mapped = gp.mapping().hats_mapped();
246        if hats_mapped == 0b0000_1111 {
247            true
248        } else if hats_mapped == 0 {
249            gp.axis_or_btn_name(Code(necs::BTN_DPAD_RIGHT)).is_none()
250                && gp.axis_or_btn_name(Code(necs::BTN_DPAD_LEFT)).is_none()
251                && gp.axis_or_btn_name(Code(necs::BTN_DPAD_DOWN)).is_none()
252                && gp.axis_or_btn_name(Code(necs::BTN_DPAD_UP)).is_none()
253                && gp.button_code(Button::DPadRight).is_none()
254        } else {
255            // Not all hats are mapped so let's ignore it for now.
256            false
257        }
258    }
259
260    let ev = ev?;
261    let gamepad = gilrs.gamepad(ev.id);
262
263    if !can_map(&gamepad) {
264        return Some(ev);
265    }
266
267    let mut out_event = ev.drop();
268
269    match ev.event {
270        EventType::AxisChanged(Axis::DPadX, val, _) => {
271            let mut release_left = false;
272            let mut release_right = false;
273
274            if val == 1.0 {
275                // The axis value might change from left (-1.0) to right (1.0) immediately without
276                // us getting an additional event for the release at the center position (0.0).
277                release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));
278
279                gilrs.insert_event(Event {
280                    event: EventType::ButtonChanged(
281                        Button::DPadRight,
282                        1.0,
283                        Code(necs::BTN_DPAD_RIGHT),
284                    ),
285                    ..ev
286                });
287                out_event = Event {
288                    event: EventType::ButtonPressed(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),
289                    ..ev
290                };
291            } else if val == -1.0 {
292                // The axis value might change from right (1.0) to left (-1.0) immediately without
293                // us getting an additional event for the release at the center position (0.0).
294                release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));
295
296                gilrs.insert_event(Event {
297                    event: EventType::ButtonChanged(
298                        Button::DPadLeft,
299                        1.0,
300                        Code(necs::BTN_DPAD_LEFT),
301                    ),
302                    ..ev
303                });
304                out_event = Event {
305                    event: EventType::ButtonPressed(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),
306                    ..ev
307                };
308            } else {
309                release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));
310                release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));
311            }
312
313            if release_right {
314                if !out_event.is_dropped() {
315                    gilrs.insert_event(out_event);
316                }
317
318                gilrs.insert_event(Event {
319                    event: EventType::ButtonChanged(
320                        Button::DPadRight,
321                        0.0,
322                        Code(necs::BTN_DPAD_RIGHT),
323                    ),
324                    ..ev
325                });
326                out_event = Event {
327                    event: EventType::ButtonReleased(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),
328                    ..ev
329                };
330            }
331
332            if release_left {
333                if !out_event.is_dropped() {
334                    gilrs.insert_event(out_event);
335                }
336
337                gilrs.insert_event(Event {
338                    event: EventType::ButtonChanged(
339                        Button::DPadLeft,
340                        0.0,
341                        Code(necs::BTN_DPAD_LEFT),
342                    ),
343                    ..ev
344                });
345                out_event = Event {
346                    event: EventType::ButtonReleased(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),
347                    ..ev
348                };
349            }
350
351            Some(out_event)
352        }
353        EventType::AxisChanged(Axis::DPadY, val, _) => {
354            let mut release_up = false;
355            let mut release_down = false;
356
357            if val == 1.0 {
358                // The axis value might change from down (-1.0) to up (1.0) immediately without us
359                // getting an additional event for the release at the center position (0.0).
360                release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));
361
362                gilrs.insert_event(Event {
363                    event: EventType::ButtonChanged(Button::DPadUp, 1.0, Code(necs::BTN_DPAD_UP)),
364                    ..ev
365                });
366                out_event = Event {
367                    event: EventType::ButtonPressed(Button::DPadUp, Code(necs::BTN_DPAD_UP)),
368                    ..ev
369                };
370            } else if val == -1.0 {
371                // The axis value might change from up (1.0) to down (-1.0) immediately without us
372                // getting an additional event for the release at the center position (0.0).
373                release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));
374
375                gilrs.insert_event(Event {
376                    event: EventType::ButtonChanged(
377                        Button::DPadDown,
378                        1.0,
379                        Code(necs::BTN_DPAD_DOWN),
380                    ),
381                    ..ev
382                });
383                out_event = Event {
384                    event: EventType::ButtonPressed(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),
385                    ..ev
386                };
387            } else {
388                release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));
389                release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));
390            }
391
392            if release_up {
393                if !out_event.is_dropped() {
394                    gilrs.insert_event(out_event);
395                }
396
397                gilrs.insert_event(Event {
398                    event: EventType::ButtonChanged(Button::DPadUp, 0.0, Code(necs::BTN_DPAD_UP)),
399                    ..ev
400                });
401                out_event = Event {
402                    event: EventType::ButtonReleased(Button::DPadUp, Code(necs::BTN_DPAD_UP)),
403                    ..ev
404                };
405            }
406
407            if release_down {
408                if !out_event.is_dropped() {
409                    gilrs.insert_event(out_event);
410                }
411
412                gilrs.insert_event(Event {
413                    event: EventType::ButtonChanged(
414                        Button::DPadDown,
415                        0.0,
416                        Code(necs::BTN_DPAD_DOWN),
417                    ),
418                    ..ev
419                });
420                out_event = Event {
421                    event: EventType::ButtonReleased(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),
422                    ..ev
423                };
424            }
425
426            Some(out_event)
427        }
428        _ => Some(ev),
429    }
430}
431
432/// Repeats pressed keys.
433#[derive(Copy, Clone, PartialEq, Eq, Debug)]
434pub struct Repeat {
435    pub after: Duration,
436    pub every: Duration,
437}
438
439impl Repeat {
440    /// Creates new `Repeat` filter with `after` set to 500ms and `every` set to 30ms.
441    pub fn new() -> Self {
442        Repeat {
443            after: Duration::from_millis(500),
444            every: Duration::from_millis(30),
445        }
446    }
447}
448
449impl Default for Repeat {
450    fn default() -> Self {
451        Self::new()
452    }
453}
454
455impl FilterFn for Repeat {
456    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
457        match ev {
458            Some(ev) => Some(ev),
459            None => {
460                let now = utils::time_now();
461                for (id, gamepad) in gilrs.gamepads() {
462                    for (nec, btn_data) in gamepad.state().buttons() {
463                        match (
464                            btn_data.is_pressed(),
465                            btn_data.is_repeating(),
466                            now.duration_since(btn_data.timestamp()),
467                        ) {
468                            (true, false, Ok(dur)) if dur >= self.after => {
469                                let btn_name = match gamepad.axis_or_btn_name(nec) {
470                                    Some(AxisOrBtn::Btn(b)) => b,
471                                    _ => Button::Unknown,
472                                };
473
474                                return Some(Event {
475                                    id,
476                                    event: EventType::ButtonRepeated(btn_name, nec),
477                                    time: btn_data.timestamp() + self.after,
478                                });
479                            }
480                            (true, true, Ok(dur)) if dur >= self.every => {
481                                let btn_name = match gamepad.axis_or_btn_name(nec) {
482                                    Some(AxisOrBtn::Btn(b)) => b,
483                                    _ => Button::Unknown,
484                                };
485
486                                return Some(Event {
487                                    id,
488                                    event: EventType::ButtonRepeated(btn_name, nec),
489                                    time: btn_data.timestamp() + self.every,
490                                });
491                            }
492                            _ => (),
493                        }
494                    }
495                }
496                None
497            }
498        }
499    }
500}
501
502/// Allow filtering events.
503///
504/// See module level documentation for more info.
505pub trait Filter {
506    fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event>;
507}
508
509/// Actual filter implementation.
510///
511/// See module level documentation for more info.
512pub trait FilterFn {
513    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event>;
514}
515
516impl<F> FilterFn for F
517where
518    F: Fn(Option<Event>, &mut Gilrs) -> Option<Event>,
519{
520    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
521        self(ev, gilrs)
522    }
523}
524
525impl Filter for Option<Event> {
526    fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {
527        let e = filter.filter(*self, gilrs);
528        debug_assert!(
529            !(self.is_some() && e.is_none()),
530            "Filter changed Some(event) into None. See ev::filter documentation for more info."
531        );
532
533        e
534    }
535}
536
537impl Filter for Event {
538    fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {
539        let e = filter.filter(Some(*self), gilrs);
540        debug_assert!(
541            e.is_some(),
542            "Filter changed Some(event) into None. See ev::filter documentation for more info."
543        );
544
545        e
546    }
547}