1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! Access to the real-time state of the joysticks.
//!
//! `joystick` provides an interface to the state of the joysticks.
//!
//! Each joystick is identified by an index that is passed to the functions of this module.
//!
//! This module allows users to query the state of joysticks at any time and directly,
//! without having to deal with a window and its events. Compared to the [`JoystickMoved`],
//! [`JoystickButtonPressed`] and [`JoystickButtonReleased`] events, `Joystick` can retrieve the
//! state of axes and buttons of joysticks at any time (you don't need to store and update a
//! boolean on your side in order to know if a button is pressed or released),
//! and you always get the real state of joysticks, even if they are moved,
//! pressed or released when your window is out of focus and no event is triggered.
//!
//! [`JoystickMoved`]: crate::window::Event::JoystickMoved
//! [`JoystickButtonPressed`]: crate::window::Event::JoystickButtonPressed
//! [`JoystickButtonReleased`]: crate::window::Event::JoystickButtonReleased
//!
//! SFML supports:
//!
//! - 8 joysticks ([`COUNT`])
//! - 32 buttons per joystick ([`BUTTON_COUNT`])
//! - 8 axes per joystick ([`AXIS_COUNT`])
//!
//! Unlike the keyboard or mouse, the state of joysticks is sometimes not directly
//! available (depending on the OS), therefore an [`update`] function must be called in order to
//! update the current state of joysticks. When you have a window with event handling, this is
//! done automatically, you don't need to call anything. But if you have no window, or if you want
//! to check joysticks state before creating one, you must call [`update`] explicitly.
//! # Usage example
//!
//! ```
//! use sfml::window::joystick;
//!
//! // If joystick #0 is connected
//! if joystick::is_connected(0) {
//!     // How many buttons does joystick #0 support?
//!     let _buttons = joystick::button_count(0);
//!     // Does joystick #0 define a X axis?
//!     let _hax_x = joystick::has_axis(0, joystick::Axis::X);
//!     // Is button #2 pressed on joystick #0?
//!     let _pressed = joystick::is_button_pressed(0, 2);
//!     // What's the current position of the Y axis on joystick #0?
//!     let _position = joystick::axis_position(0, joystick::Axis::Y);
//! }
//! ```
//!
//! [`COUNT`]: COUNT
//! [`BUTTON_COUNT`]: BUTTON_COUNT
//! [`AXIS_COUNT`]: AXIS_COUNT
//! [`update`]: update
//!

use crate::{ffi::window as ffi, SfBox};

/// Maximum number of supported joysticks.
pub const COUNT: u32 = 8;
/// Maximum number of supported buttons.
pub const BUTTON_COUNT: u32 = 32;
/// Maximum number of supported axes.
pub const AXIS_COUNT: u32 = 8;

/// Axes supported by SFML joysticks
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Hash)]
#[repr(transparent)]
pub struct Axis(pub(super) ffi::sfJoystickAxis);

impl Axis {
    /// The X axis.
    pub const X: Self = Self(ffi::sfJoystickAxis::sfJoystickX);
    /// The Y axis.
    pub const Y: Self = Self(ffi::sfJoystickAxis::sfJoystickY);
    /// The Z axis.
    pub const Z: Self = Self(ffi::sfJoystickAxis::sfJoystickZ);
    /// The R axis.
    pub const R: Self = Self(ffi::sfJoystickAxis::sfJoystickR);
    /// The U axis.
    pub const U: Self = Self(ffi::sfJoystickAxis::sfJoystickU);
    /// The V axis.
    pub const V: Self = Self(ffi::sfJoystickAxis::sfJoystickV);
    /// The X axis of the point-of-view hat.
    pub const POV_X: Self = Self(ffi::sfJoystickAxis::sfJoystickPovX);
    /// The Y axis of the point-of-view hat.
    pub const POV_Y: Self = Self(ffi::sfJoystickAxis::sfJoystickPovY);
}

/// Check if the joystick is connected
///
/// # Arguments
/// * joystick - Index of the joystick to check
///
/// Return true if the joystick is connected, false otherwise
#[must_use]
pub fn is_connected(joystick: u32) -> bool {
    unsafe { ffi::sfJoystick_isConnected(joystick) }
}

/// Return the number of buttons supported by a joystick
///
/// # Arguments
/// * joystick - Index of the joystick
///
/// Return the number of buttons supported by the joystick.
#[must_use]
pub fn button_count(joystick: u32) -> u32 {
    unsafe { ffi::sfJoystick_getButtonCount(joystick) }
}

/// Check if the joystick support a given axis
///
/// If the joystick is not connected, this function returns false.
///
/// # Arguments
/// * joystick - Index of the joystick
/// * axis - Axis to check
///
/// Return true if the joystick supports the axis, false otherwise
#[must_use]
pub fn has_axis(joystick: u32, axis: Axis) -> bool {
    unsafe { ffi::sfJoystick_hasAxis(joystick, axis.0) }
}

/// Check if the button is pressed on a given joystick.
///
/// If the joystick is not connected, this function returns false.
///
/// # Arguments
/// * joystick - Index of the joystick
/// * button - Button to check
///
/// Return true if the button is pressed, false otherwise
#[must_use]
pub fn is_button_pressed(joystick: u32, button: u32) -> bool {
    unsafe { ffi::sfJoystick_isButtonPressed(joystick, button) }
}

/// Get the current position on a given axis, on a given joystick.
///
/// If the joystick is not connected, this function returns 0.
///
/// # Arguments
/// * joystick - Index of the joystick
/// * axis - Axis to check
///
/// Return the current position of the axis, in range [-100 .. 100]
#[must_use]
pub fn axis_position(joystick: u32, axis: Axis) -> f32 {
    unsafe { ffi::sfJoystick_getAxisPosition(joystick, axis.0) }
}

/// Update the states of all joysticks
///
/// This function is used internally by SFML, so you normally
/// don't have to call it explicitely. However, you may need to
/// call it if you have no window yet (or no window at all):
/// in this case the joysticks states are not updated automatically.
pub fn update() {
    unsafe {
        ffi::sfJoystick_update();
    }
}

/// Get the joystick information.
#[must_use]
pub fn identification(joystick: u32) -> SfBox<ffi::JoystickIdentification> {
    unsafe {
        SfBox::new(ffi::sfJoystick_getIdentification(joystick))
            .expect("Failed to create JoystickIdentification")
    }
}

impl ffi::JoystickIdentification {
    pub fn name(&self) -> &crate::ffi::system::sfString {
        unsafe { &*ffi::sfJoystickIdentification_getName(self) }
    }
    pub fn vendor_id(&self) -> u32 {
        unsafe { ffi::sfJoystickIdentification_getVendorId(self) }
    }
    pub fn product_id(&self) -> u32 {
        unsafe { ffi::sfJoystickIdentification_getProductId(self) }
    }
}