1 module unecht.glfw.joysticks;
2 
3 import unecht.core.events;
4 
5 import derelict.glfw3.glfw3;
6 
7 static immutable MAX_AXES = 8;
8 static immutable MAX_BUTTONS = 12;
9 
10 ///
11 struct UEJoystickInfo
12 {
13     uint axesCount;
14     uint buttonCount;
15     string name;
16 }
17 
18 ///
19 struct UEJoystickState
20 {
21     UEJoystickInfo info;
22 
23     bool connected=false;
24     float* glfwAxesPtr;
25     ubyte* glfwButtonPtr;
26     float[MAX_AXES] axesState;
27     ubyte[MAX_BUTTONS] buttonState;
28 }
29 
30 ///
31 struct GLFWJoysticks
32 {
33     private UEJoystickState[4] _states;
34     private UEEventsSystem _events;
35 
36     void init(UEEventsSystem events)
37     {
38         _events = events;
39     }
40 
41     void update()
42     {
43         foreach(int id, ref state; _states)
44         {
45             auto presentNow = glfwJoystickPresent (id);
46 
47             if(presentNow && !state.connected)
48             {
49                 state.connected = true;
50                 connect(id,state);
51                 continue;
52             }
53             else if(!presentNow && state.connected)
54             {
55                 disconnect(id,state.info.name);
56                 state.info.name.length=0;
57                 state.connected = false;
58                 state.glfwAxesPtr = null;
59                 state.glfwButtonPtr = null;
60                 state.axesState[] = 0.0f;
61                 state.buttonState[] = 0;
62                 continue;
63             }
64 
65             if(presentNow)
66             {
67                 int axesCount;
68                 auto axesPtr = glfwGetJoystickAxes (id, &axesCount);
69                 auto axesState = axesPtr[0..state.info.axesCount];
70 
71                 int buttonCount;
72                 auto buttonPtr = glfwGetJoystickButtons (id, &buttonCount);
73                 auto buttonState = buttonPtr[0..state.info.buttonCount];
74 
75                 if(buttonState != state.buttonState[0..state.info.buttonCount])
76                 {
77                     triggerButtonEvent(buttonState,state);
78                     state.buttonState[0..state.info.buttonCount] = buttonState;
79                 }
80 
81                 if(axesState != state.axesState[0..state.info.axesCount])
82                 {
83                     state.axesState[0..state.info.axesCount] = axesState;
84                     triggerAxesEvent(id,axesState,state);
85                 }
86             }
87         }
88     }
89 
90     void triggerAxesEvent(int id, float[] newState, in UEJoystickState state)
91     {
92         UEEvent ev;
93         ev.eventType = UEEventType.joystickAxes;
94         ev.joystickAxes.axes = newState;
95         ev.joystickAxes.id = id;
96 
97         _events.trigger(ev);
98     }
99 
100     void triggerButtonEvent(in ubyte[] newState, in UEJoystickState state)
101     {
102         foreach(uint i,button; newState)
103         {
104             if(newState[i] != state.buttonState[i])
105             {
106                 UEEvent ev;
107                 ev.eventType = UEEventType.joystickButton;
108                 ev.joystickButton.buttonId = i;
109                 ev.joystickButton.pressed = button==GLFW_PRESS;
110                 
111                 _events.trigger(ev);
112             }
113         }
114     }
115 
116     void connect(int id, ref UEJoystickState state)
117     {
118         state.info.name.length=0;
119         auto cName = glfwGetJoystickName (id);
120         if(cName)
121         {
122             import std.conv;
123             state.info.name = to!string(cName);
124         }
125 
126         int axesCount;
127         state.glfwAxesPtr = glfwGetJoystickAxes (id, &axesCount);
128 
129         int buttonCount;
130         state.glfwButtonPtr = glfwGetJoystickButtons (id, &buttonCount);
131 
132         import std.algorithm:min;
133         state.info.axesCount = min(axesCount,MAX_AXES);
134         state.info.buttonCount = min(buttonCount,MAX_BUTTONS);
135 
136         //trigger event
137         {
138             UEEvent ev;
139             ev.eventType = UEEventType.joystickStatus;
140             ev.joystickStatus.connected = true;
141 
142             ev.joystickStatus.name = state.info.name;
143             ev.joystickStatus.id = id;
144             ev.joystickStatus.buttonCount = state.info.buttonCount;
145             ev.joystickStatus.axesCount = state.info.axesCount;
146 
147             _events.trigger(ev);
148         }
149     }
150 
151     void disconnect(int id, string name)
152     {
153         UEEvent ev;
154         ev.eventType = UEEventType.joystickStatus;
155         ev.joystickStatus.connected = false;
156         
157         ev.joystickStatus.name = name;
158         ev.joystickStatus.id = id;
159 
160         _events.trigger(ev);
161     }
162 }