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