gilrs/ev/
mod.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//! Gamepad state and other event related functionality.
9
10pub mod filter;
11pub mod state;
12
13use std::{
14    fmt::{Display, Formatter, Result as FmtResult},
15    time::SystemTime,
16};
17
18use crate::{constants::*, gamepad::GamepadId, utils};
19
20#[cfg(feature = "serde-serialize")]
21use serde::{Deserialize, Serialize};
22
23/// Platform specific event code.
24///
25/// This type represents single gamepads's element like specific axis or button.
26/// It can't be directly created, but you can get it from events or using
27/// `Gamepad`'s methods [`button_code`](crate::Gamepad::button_code) and
28/// [`axis_code`](crate::Gamepad::axis_code). If `serde-serialize` feature is
29/// enabled, `Code` can be serialized and deserialized, but keep in mind that
30/// layout **is** platform-specific. So it's not possible to serialize `Code` on
31/// Linux and deserialize it on Windows. This also apply to `Display` implementation.
32#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
33#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
34pub struct Code(pub(crate) gilrs_core::EvCode);
35
36impl Display for Code {
37    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
38        self.0.fmt(f)
39    }
40}
41
42impl Code {
43    /// Return platform-specific event code packed into a single `u32`.
44    pub fn into_u32(&self) -> u32 {
45        self.0.into_u32()
46    }
47}
48
49/// Holds information about gamepad event.
50#[derive(Copy, Clone, PartialEq, Debug)]
51#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
52#[non_exhaustive]
53pub struct Event {
54    /// Id of gamepad.
55    pub id: GamepadId,
56    /// Event's data.
57    pub event: EventType,
58    /// Time when event was emitted.
59    pub time: SystemTime,
60}
61
62impl Event {
63    /// Creates new event with current time.
64    pub fn new(id: GamepadId, event: EventType) -> Self {
65        Event {
66            id,
67            event,
68            time: utils::time_now(),
69        }
70    }
71
72    /// Returns `Event` with `EventType::Dropped`.
73    pub fn drop(mut self) -> Event {
74        self.event = EventType::Dropped;
75
76        self
77    }
78
79    /// Returns true if event is `Dropped` and should be ignored.
80    pub fn is_dropped(&self) -> bool {
81        self.event == EventType::Dropped
82    }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq)]
86#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
87#[non_exhaustive]
88/// Gamepad event.
89pub enum EventType {
90    /// Some button on gamepad has been pressed.
91    ButtonPressed(Button, Code),
92    /// This event can be generated by [`ev::Repeat`](filter/struct.Repeat.html) event filter.
93    ButtonRepeated(Button, Code),
94    /// Previously pressed button has been released.
95    ButtonReleased(Button, Code),
96    /// Value of button has changed. Value can be in range \[0.0, 1.0\].
97    ButtonChanged(Button, f32, Code),
98    /// Value of axis has changed. Value can be in range \[-1.0, 1.0\].
99    AxisChanged(Axis, f32, Code),
100    /// Gamepad has been connected. If gamepad's UUID doesn't match one of disconnected gamepads,
101    /// newly connected gamepad will get new ID.
102    Connected,
103    /// Gamepad has been disconnected. Disconnected gamepad will not generate any new events.
104    Disconnected,
105    /// There was an `Event`, but it was dropped by one of filters. You should ignore it.
106    Dropped,
107    /// A force feedback effect has ran for its duration and stopped.
108    ForceFeedbackEffectCompleted,
109}
110
111#[repr(u16)]
112#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
113#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
114/// Gamepad's elements which state can be represented by value from 0.0 to 1.0.
115///
116/// ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)
117pub enum Button {
118    // Action Pad
119    South = BTN_SOUTH,
120    East = BTN_EAST,
121    North = BTN_NORTH,
122    West = BTN_WEST,
123    C = BTN_C,
124    Z = BTN_Z,
125    // Triggers
126    LeftTrigger = BTN_LT,
127    LeftTrigger2 = BTN_LT2,
128    RightTrigger = BTN_RT,
129    RightTrigger2 = BTN_RT2,
130    // Menu Pad
131    Select = BTN_SELECT,
132    Start = BTN_START,
133    Mode = BTN_MODE,
134    // Sticks
135    LeftThumb = BTN_LTHUMB,
136    RightThumb = BTN_RTHUMB,
137    // D-Pad
138    DPadUp = BTN_DPAD_UP,
139    DPadDown = BTN_DPAD_DOWN,
140    DPadLeft = BTN_DPAD_LEFT,
141    DPadRight = BTN_DPAD_RIGHT,
142
143    #[default]
144    Unknown = BTN_UNKNOWN,
145}
146
147impl Button {
148    pub fn is_action(self) -> bool {
149        use crate::Button::*;
150        matches!(self, South | East | North | West | C | Z)
151    }
152
153    pub fn is_trigger(self) -> bool {
154        use crate::Button::*;
155        matches!(
156            self,
157            LeftTrigger | LeftTrigger2 | RightTrigger | RightTrigger2
158        )
159    }
160
161    pub fn is_menu(self) -> bool {
162        use crate::Button::*;
163        matches!(self, Select | Start | Mode)
164    }
165
166    pub fn is_stick(self) -> bool {
167        use crate::Button::*;
168        matches!(self, LeftThumb | RightThumb)
169    }
170
171    pub fn is_dpad(self) -> bool {
172        use crate::Button::*;
173        matches!(self, DPadUp | DPadDown | DPadLeft | DPadRight)
174    }
175
176    pub fn to_nec(self) -> Option<Code> {
177        use gilrs_core::native_ev_codes as necs;
178
179        match self {
180            Button::South => Some(necs::BTN_SOUTH),
181            Button::East => Some(necs::BTN_EAST),
182            Button::North => Some(necs::BTN_NORTH),
183            Button::West => Some(necs::BTN_WEST),
184            Button::C => Some(necs::BTN_C),
185            Button::Z => Some(necs::BTN_Z),
186            Button::LeftTrigger => Some(necs::BTN_LT),
187            Button::LeftTrigger2 => Some(necs::BTN_LT2),
188            Button::RightTrigger => Some(necs::BTN_RT),
189            Button::RightTrigger2 => Some(necs::BTN_RT2),
190            Button::Select => Some(necs::BTN_SELECT),
191            Button::Start => Some(necs::BTN_START),
192            Button::Mode => Some(necs::BTN_MODE),
193            Button::LeftThumb => Some(necs::BTN_LTHUMB),
194            Button::RightThumb => Some(necs::BTN_RTHUMB),
195            Button::DPadUp => Some(necs::BTN_DPAD_UP),
196            Button::DPadDown => Some(necs::BTN_DPAD_DOWN),
197            Button::DPadLeft => Some(necs::BTN_DPAD_LEFT),
198            Button::DPadRight => Some(necs::BTN_DPAD_RIGHT),
199            _ => None,
200        }
201        .map(Code)
202    }
203}
204
205#[repr(u16)]
206#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
207#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
208/// Gamepad's elements which state can be represented by value from -1.0 to 1.0.
209///
210/// ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)
211pub enum Axis {
212    LeftStickX = AXIS_LSTICKX,
213    LeftStickY = AXIS_LSTICKY,
214    LeftZ = AXIS_LEFTZ,
215    RightStickX = AXIS_RSTICKX,
216    RightStickY = AXIS_RSTICKY,
217    RightZ = AXIS_RIGHTZ,
218    DPadX = AXIS_DPADX,
219    DPadY = AXIS_DPADY,
220    Unknown = AXIS_UNKNOWN,
221}
222
223impl Axis {
224    /// Returns true if axis is `LeftStickX`, `LeftStickY`, `RightStickX` or `RightStickY`.
225    pub fn is_stick(self) -> bool {
226        use crate::Axis::*;
227        matches!(self, LeftStickX | LeftStickY | RightStickX | RightStickY)
228    }
229
230    /// Returns the other axis from same element of gamepad, if any.
231    ///
232    /// | input       | output            |
233    /// |-------------|-------------------|
234    /// |`LeftStickX` |`Some(LeftStickY)` |
235    /// |`LeftStickY` |`Some(LeftStickX)` |
236    /// |`RightStickX`|`Some(RightStickY)`|
237    /// |`RightStickY`|`Some(RightStickX)`|
238    /// |`DpadX`      |`Some(DpadY)`      |
239    /// |`DpadY`      |`Some(DpadX)`      |
240    /// | …           |`None`             |
241    pub fn second_axis(self) -> Option<Self> {
242        use crate::Axis::*;
243        match self {
244            LeftStickX => Some(LeftStickY),
245            LeftStickY => Some(LeftStickX),
246            RightStickX => Some(RightStickY),
247            RightStickY => Some(RightStickX),
248            DPadX => Some(DPadY),
249            DPadY => Some(DPadX),
250            _ => None,
251        }
252    }
253}
254
255/// Represents `Axis` or `Button`.
256#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
257#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
258pub enum AxisOrBtn {
259    Axis(Axis),
260    Btn(Button),
261}
262
263impl AxisOrBtn {
264    pub(crate) fn is_button(&self) -> bool {
265        matches!(self, AxisOrBtn::Btn(_))
266    }
267}