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