1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2018 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  *
24  *
25  * DSFML is based on SFML (Copyright Laurent Gomila)
26  */
27 
28 /**
29  * $(U Joystick) provides an interface to the state of the joysticks. It only
30  * contains static functions, so it's not meant to be instanciated. Instead,
31  * each joystick is identified by an index that is passed to the functions of
32  * this class.
33  *
34  * This class allows users to query the state of joysticks at any time and
35  * directly, without having to deal with a window and its events. Compared to
36  * the `JoystickMoved`, `JoystickButtonPressed`, and `JoystickButtonReleased`
37  * events, $(U Joystick) can retrieve the state of axes and buttons of joysticks
38  * at any time (you don't need to store and update a boolean on your side in
39  * order to know if a button is pressed or released), and you always get the
40  * real state of joysticks, even if they are moved, pressed or released when
41  * your window is out of focus and no event is triggered.
42  *
43  * DSFML supports:
44  * $(UL
45  * $(LI 8 joysticks (Joystick.Count))
46  * $(LI 32 buttons per joystick (Joystick.ButtonCount))
47  * $(LI 8 axes per joystick (Joystick.AxisCount)))
48  *
49  * $(PARA
50  * Unlike the keyboard or mouse, the state of joysticks is sometimes not
51  * directly available (depending on the OS), therefore an `update()` function
52  * must be called in order to update the current state of joysticks. When you
53  * have a window with event handling, this is done automatically, you don't need
54  * to call anything. But if you have no window, or if you want to check
55  * joysticks state before creating one, you must call `Joystick.update`
56  * explicitly.)
57  *
58  * Example:
59  * ---
60  * // Is joystick #0 connected?
61  * bool connected = Joystick.isConnected(0);
62  *
63  * // How many buttons does joystick #0 support?
64  * uint buttons = Joystick.getButtonCount(0);
65  *
66  * // Does joystick #0 define a X axis?
67  * bool hasX = Joystick.hasAxis(0, Joystick.Axis.X);
68  *
69  * // Is button #2 pressed on joystick #0?
70  * bool pressed = Joystick.isButtonPressed(0, 2);
71  *
72  * // What's the current position of the Y axis on joystick #0?
73  * float position = Joystick.getAxisPosition(0, Joystick.Axis.Y);
74  * ---
75  *
76  * See_Also:
77  * $(KEYBOAD_LINK), $(MOUSE_LINK)
78  */
79 module dsfml.window.joystick;
80 
81 /**
82  * Give access to the real-time state of the joysticks.
83  */
84 final abstract class Joystick
85 {
86     /// Structure holding a joystick's identification
87     struct Identification
88     {
89         private static dstring[immutable(uint)[2]] nameCache;
90 
91         /// Index of the joystick.
92         uint index;
93 
94         /// Name of the joystick.
95         @property dstring name() const
96         {
97             //In theory, each vid:pid combination should only have one name associated with it.
98             //slightly arcane syntax to make older GDC happy.
99             uint[2] tempkey;
100             tempkey[0] = vendorId;
101             tempkey[1] = productId;
102             immutable(uint)[2] key = tempkey;
103 
104             dstring* cachedName = (key in nameCache);
105             if (cachedName !is null) {
106                 return *cachedName;
107             } else {
108                 import std.exception;
109 
110                 dchar[] retrievedName;
111                 dstring retval;
112 
113                 retrievedName.length = sfJoystick_getIdentificationNameLength(index);
114 
115                 sfJoystick_getIdentificationName(index, retrievedName.ptr);
116 
117                 nameCache[key] = retval = assumeUnique(retrievedName);
118 
119                 return retval;
120             }
121         }
122 
123         /// Manufacturer identifier.
124         uint vendorId;
125 
126         /// Product identifier.
127         uint productId;
128     }
129 
130     //Constants related to joysticks capabilities.
131     enum
132     {
133         /// Maximum number of supported joysticks.
134         JoystickCount = 8,
135         /// Maximum number of supported buttons.
136         JoystickButtonCount = 32,
137         /// Maximum number of supported axes.
138         JoystickAxisCount = 8
139     }
140 
141     /// Axes supported by SFML joysticks.
142     enum Axis
143     {
144         /// The X axis.
145         X,
146         /// The Y axis.
147         Y,
148         /// The Z axis.
149         Z,
150         /// The R axis.
151         R,
152         /// The U axis.
153         U,
154         /// The V axis.
155         V,
156         /// The X axis of the point-of-view hat.
157         PovX,
158         /// The Y axis of the point-of-view hat.
159         PovY
160     }
161 
162     /**
163      * Return the number of buttons supported by a joystick.
164      *
165      * If the joystick is not connected, this function returns 0.
166      *
167      * Params:
168      * 		joystick = Index of the joystick
169      *
170      * Returns: Number of buttons supported by the joystick.
171      */
172     static uint getButtonCount(uint joystick)
173     {
174         return sfJoystick_getButtonCount(joystick);
175     }
176 
177     /**
178      * Get the current position of a joystick axis.
179      *
180      * If the joystick is not connected, this function returns 0.
181      *
182      * Params:
183      * 		joystick = 	Index of the joystick
184      * 		axis     = Axis to check
185      *
186      * Returns: Current position of the axis, in range [-100 .. 100].
187      */
188     static float getAxisPosition(uint joystick, Axis axis)
189     {
190         return sfJoystick_getAxisPosition(joystick, axis);
191     }
192 
193     /**
194      * Get the joystick information
195      *
196      * Params:
197      * 		joystick = Index of the joystick
198      *
199      * Returns: Structure containing the joystick information.
200      */
201     static Identification getIdentification(uint joystick) {
202         Identification identification;
203 
204         sfJoystick_getIdentification(joystick, &identification.vendorId, &identification.productId);
205 
206         return identification;
207     }
208 
209     /**
210      * Check if a joystick supports a given axis.
211      *
212      * If the joystick is not connected, this function returns false.
213      *
214      * Params:
215      * 		joystick = 	Index of the joystick
216      * 		axis = Axis to check
217      *
218      * Returns: true if the joystick supports the axis, false otherwise.
219      */
220     static bool hasAxis(uint joystick, Axis axis)
221     {
222         return (sfJoystick_hasAxis(joystick, axis));
223     }
224 
225     /**
226      * Check if a joystick button is pressed.
227      *
228      * If the joystick is not connected, this function returns false.
229      *
230      * Params:
231      * 		joystick = 	Index of the joystick
232      * 		button = Button to check
233      *
234      * Returns: true if the button is pressed, false otherwise.
235      */
236     static bool isButtonPressed(uint joystick, uint button)
237     {
238         return sfJoystick_isButtonPressed(joystick, button);
239     }
240 
241     /**
242      * Check if a joystick is connected.
243      *
244      * Params:
245      * 		joystick = 	Index of the joystick
246      *
247      * Returns: true if the joystick is connected, false otherwise.
248      */
249     static bool isConnected(uint joystick)
250     {
251         return (sfJoystick_isConnected(joystick));
252     }
253 
254     /**
255      * Update the states of all joysticks.
256      *
257      * This function is used internally by SFML, so you normally don't have to
258      * call it explicitely.
259      *
260      * However, you may need to call it if you have no window yet (or no window
261      * at all): in this case the joysticks states are not updated automatically.
262      */
263     static void update()
264     {
265         sfJoystick_update();
266     }
267 
268 }
269 
270 unittest
271 {
272     version(DSFML_Unittest_Window)
273     {
274         import std.stdio;
275 
276         Joystick.update();
277 
278         bool[] joysticks = [false,false,false,false,false,false,false,false];
279 
280         for(uint i; i < Joystick.JoystickCount; ++i)
281         {
282             if(Joystick.isConnected(i))
283             {
284                 auto id = Joystick.getIdentification(i);
285                 joysticks[i] = true;
286                 writeln("Joystick number ",i," is connected!");
287                 writefln("Type: %s, ID: %x:%x", id.name, id.vendorId, id.productId);
288             }
289         }
290 
291         foreach(uint i,bool joystick;joysticks)
292         {
293             if(joystick)
294             {
295                 //Check buttons
296                 uint buttonCounts = Joystick.getButtonCount(i);
297 
298                 for(uint j = 0; j<buttonCounts; ++j)
299                 {
300                     if(Joystick.isButtonPressed(i,j))
301                     {
302                         writeln("Button ", j, " was pressed on joystick ", i);
303                     }
304                 }
305 
306                 //check axis
307                 for(int j = 0; j<Joystick.JoystickAxisCount;++j)
308                 {
309                     Joystick.Axis axis = cast(Joystick.Axis)j;
310 
311                     if(Joystick.hasAxis(i,axis))
312                     {
313                         writeln("Axis ", axis, " has a position of ", Joystick.getAxisPosition(i,axis), "for joystick", i);
314                     }
315                 }
316             }
317         }
318     }
319 }
320 
321 private extern(C):
322 //Check if a joystick is connected
323 bool sfJoystick_isConnected(uint joystick);
324 
325 //Return the number of buttons supported by a joystick
326 uint sfJoystick_getButtonCount(uint joystick);
327 
328 //Return the length of the joystick identification structure's name
329 size_t sfJoystick_getIdentificationNameLength(uint joystick);
330 
331 //Write the name of the joystick into a D-allocated string buffer.
332 void sfJoystick_getIdentificationName (uint joystick, dchar* nameBuffer);
333 
334 //Return the identification structure for a joystick
335 void sfJoystick_getIdentification(uint joystick, uint* vendorID, uint* productId);
336 
337 //Check if a joystick supports a given axis
338 bool sfJoystick_hasAxis(uint joystick, int axis);
339 
340 //Check if a joystick button is pressed
341 bool sfJoystick_isButtonPressed(uint joystick, uint button);
342 
343 //Get the current position of a joystick axis
344 float sfJoystick_getAxisPosition(uint joystick, int axis);
345 
346 //Update the states of all joysticks
347 void sfJoystick_update();