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