1 module prova.input.controller;
2 
3 import derelict.sdl2.sdl,
4        prova.math,
5        std.conv;
6 
7 ///
8 enum ThumbStick { LEFT, RIGHT }
9 
10 ///
11 enum ControllerButton {
12   A, B, X, Y, BACK, GUIDE, START,
13   LEFT_STICK, RIGHT_STICK, BUMPER_LEFT, BUMPER_RIGHT,
14   DPAD_UP, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT,
15   // simulated buttons
16   TRIGGER_LEFT, TRIGGER_RIGHT
17 }
18 
19 ///
20 class Controller
21 {
22   ///
23   float deadzone = .15;
24   private SDL_GameController* handle;
25   private int[18] oldButtonState;
26   private int[18] buttonState;
27   private int _id;
28 
29   package this(int index)
30   {
31     _id = index;
32     handle = SDL_GameControllerOpen(index);
33     
34     foreach(i; 0 .. 17) {
35       oldButtonState[i] = false;
36       buttonState[i] = false;
37     }
38   }
39 
40   ///
41   @property int id()
42   {
43     return _id;
44   }
45 
46   ///
47   bool isConnected()
48   {
49     return cast(bool) SDL_GameControllerGetAttached(handle);
50   }
51 
52   package void update()
53   {
54     foreach(i; 0 .. 17) {
55       oldButtonState[i] = buttonState[i];
56       buttonState[i] = getButtonState(cast(ControllerButton) i);
57     }
58   }
59 
60   private bool getButtonState(ControllerButton button)
61   {
62     if(button == ControllerButton.TRIGGER_LEFT || button == ControllerButton.TRIGGER_RIGHT)
63       return getTrigger(button) > .3;
64     return cast(bool) SDL_GameControllerGetButton(handle, cast(SDL_GameControllerButton) button);
65   }
66 
67   ///
68   bool isButtonDown(ControllerButton button)
69   {
70     return cast(bool) buttonState[cast(int) button];
71   }
72 
73   ///
74   bool isButtonUp(ControllerButton button)
75   {
76     return !buttonState[cast(int) button];
77   }
78 
79   ///
80   bool buttonJustPressed(ControllerButton button)
81   {
82     return buttonState[cast(int) button] && !oldButtonState[cast(int) button];
83   }
84 
85   ///
86   Vector2 getStick(ThumbStick stick)
87   {
88     int xAxis = SDL_CONTROLLER_AXIS_LEFTX;
89 
90     if(stick == ThumbStick.RIGHT)
91       xAxis = SDL_CONTROLLER_AXIS_RIGHTX;
92     
93     Vector2 displacement = Vector2(getAxis(xAxis), -getAxis(xAxis + 1));
94 
95     if(displacement.getMagnitude() <= deadzone)
96       return Vector2();
97     else
98       return displacement;
99   }
100 
101   ///
102   float getTrigger(ControllerButton button)
103   {
104     if(button == ControllerButton.TRIGGER_LEFT)
105       return getAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT);
106     if(button == ControllerButton.TRIGGER_RIGHT)
107       return getAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
108     
109     throw new Exception(to!string(button) ~ " is not a trigger");
110   }
111 
112   private float getAxis(int axis)
113   {
114     return SDL_GameControllerGetAxis(handle, cast(SDL_GameControllerAxis) axis) / cast(float) 32767;
115   }
116 
117   ~this()
118   {
119     SDL_GameControllerClose(handle);
120   }
121 }