forked from MrStahlfelge/Jamepad
-
Notifications
You must be signed in to change notification settings - Fork 11
/
ControllerIndex.java
337 lines (295 loc) · 13.1 KB
/
ControllerIndex.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
package com.studiohartman.jamepad;
/**
* This class is the main thing you're gonna need to deal with if you want lots of
* control over your gamepads or want to avoid lots of ControllerState allocations.
*
* A Controller index cannot be made from outside the Jamepad package. You're gonna need to go
* through a ControllerManager to get your controllers.
*
* A ControllerIndex represents the controller at a given index. There may or may not actually
* be a controller at that index. Exceptions are thrown if the controller is not connected.
*
* @author William Hartman
*/
public final class ControllerIndex {
/*JNI
#include "SDL.h"
*/
private static final float AXIS_MAX_VAL = 32767;
private int index;
private long controllerPtr;
private boolean[] heldDownButtons;
private boolean[] justPressedButtons;
/**
* Constructor. Builds a controller at the given index and attempts to connect to it.
* This is only accessible in the Jamepad package, so people can't go trying to make controllers
* before the native library is loaded or initialized.
*
* @param index The index of the controller
*/
ControllerIndex(int index) {
this.index = index;
heldDownButtons = new boolean[ControllerButton.values().length];
justPressedButtons = new boolean[ControllerButton.values().length];
for(int i = 0; i < heldDownButtons.length; i++) {
heldDownButtons[i] = false;
justPressedButtons[i] = false;
}
connectController();
}
private void connectController() {
controllerPtr = nativeConnectController(index);
}
private native long nativeConnectController(int index); /*
return (jlong) SDL_GameControllerOpen(index);
*/
/**
* Close the connection to this controller.
*/
public void close() {
if(controllerPtr != 0) {
nativeClose(controllerPtr);
controllerPtr = 0;
}
}
private native void nativeClose(long controllerPtr); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
if(pad && SDL_GameControllerGetAttached(pad)) {
SDL_GameControllerClose(pad);
}
pad = NULL;
*/
/**
* Close and reconnect to the native gamepad at the index associated with this ControllerIndex object.
* This is will refresh the gamepad represented here. This should be called if something is plugged
* in or unplugged.
*
* @return whether or not the controller could successfully reconnect.
*/
public boolean reconnectController() {
close();
connectController();
return isConnected();
}
/**
* Return whether or not the controller is currently connected. This first checks that the controller
* was successfully connected to our SDL backend. Then we check if the controller is currently plugged
* in.
*
* @return Whether or not the controller is plugged in.
*/
public boolean isConnected() {
return controllerPtr != 0 && nativeIsConnected(controllerPtr);
}
private native boolean nativeIsConnected(long controllerPtr); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
if (pad && SDL_GameControllerGetAttached(pad)) {
return JNI_TRUE;
}
return JNI_FALSE;
*/
/**
* Returns the index of the current controller.
* @return The index of the current controller.
*/
public int getIndex() {
return index;
}
/**
* @return true of controller can vibrate
* @throws ControllerUnpluggedException If the controller is not connected
*/
public boolean canVibrate() throws ControllerUnpluggedException {
ensureConnected();
return nativeCanVibrate(controllerPtr);
}
private native boolean nativeCanVibrate(long controllerPtr); /*
SDL_Joystick* joystick = SDL_GameControllerGetJoystick((SDL_GameController*) controllerPtr);
return SDL_JoystickHasRumble(joystick);
*/
private native boolean nativeDoVibration(long controllerPtr, int leftMagnitude, int rightMagnitude, int duration_ms); /*
SDL_Joystick* joystick = SDL_GameControllerGetJoystick((SDL_GameController*) controllerPtr);
return SDL_JoystickRumble(joystick, leftMagnitude, rightMagnitude, duration_ms) == 0;
*/
/**
* Vibrate the controller using the new rumble API
* Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
*
* This will return false if the controller doesn't support vibration or if SDL was unable to start
* vibration (maybe the controller doesn't support left/right vibration, maybe it was unplugged in the
* middle of trying, etc...)
*
* @param leftMagnitude The intensity of the left rumble motor (this should be between 0 and 1)
* @param rightMagnitude The intensity of the right rumble motor (this should be between 0 and 1)
* @return Whether or not the controller was able to be vibrated (i.e. if haptics are supported)
* @throws ControllerUnpluggedException If the controller is not connected
*/
public boolean doVibration(float leftMagnitude, float rightMagnitude, int duration_ms) throws ControllerUnpluggedException {
ensureConnected();
//Check the values are appropriate
boolean leftInRange = leftMagnitude >= 0 && leftMagnitude <= 1;
boolean rightInRange = rightMagnitude >= 0 && rightMagnitude <= 1;
if(!(leftInRange && rightInRange)) {
throw new IllegalArgumentException("The passed values are not in the range 0 to 1!");
}
return nativeDoVibration(controllerPtr, (int) (65535 * leftMagnitude), (int) (65535 * rightMagnitude), duration_ms);
}
/**
* Returns whether or not a given button has been pressed.
*
* @param toCheck The ControllerButton to check the state of
* @return Whether or not the button is pressed.
* @throws ControllerUnpluggedException If the controller is not connected
*/
public boolean isButtonPressed(ControllerButton toCheck) throws ControllerUnpluggedException {
updateButton(toCheck.ordinal());
return heldDownButtons[toCheck.ordinal()];
}
/**
* Returns whether or not a given button has just been pressed since you last made a query
* about that button (either through this method, isButtonPressed(), or through the ControllerState
* side of things). If the button was not pressed the last time you checked but is now, this method
* will return true.
*
* @param toCheck The ControllerButton to check the state of
* @return Whether or not the button has just been pressed.
* @throws ControllerUnpluggedException If the controller is not connected
*/
public boolean isButtonJustPressed(ControllerButton toCheck) throws ControllerUnpluggedException {
updateButton(toCheck.ordinal());
return justPressedButtons[toCheck.ordinal()];
}
private void updateButton(int buttonIndex) throws ControllerUnpluggedException {
ensureConnected();
boolean currButtonIsPressed = nativeCheckButton(controllerPtr, buttonIndex);
justPressedButtons[buttonIndex] = (currButtonIsPressed && !heldDownButtons[buttonIndex]);
heldDownButtons[buttonIndex] = currButtonIsPressed;
}
private native boolean nativeCheckButton(long controllerPtr, int buttonIndex); /*
SDL_GameControllerUpdate();
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return SDL_GameControllerGetButton(pad, (SDL_GameControllerButton) buttonIndex);
*/
/**
* Returns if a given button is available on controller.
*
* @param toCheck The ControllerButton to check
* @throws ControllerUnpluggedException If the controller is not connected
*/
public boolean isButtonAvailable(ControllerButton toCheck) throws ControllerUnpluggedException {
ensureConnected();
return nativeButtonAvailable(controllerPtr, toCheck.ordinal());
}
private native boolean nativeButtonAvailable(long controllerPtr, int buttonIndex); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return SDL_GameControllerHasButton(pad, (SDL_GameControllerButton) buttonIndex);
*/
/**
* Returns the current state of a passed axis.
*
* @param toCheck The ControllerAxis to check the state of
* @return The current state of the requested axis.
* @throws ControllerUnpluggedException If the controller is not connected
*/
public float getAxisState(ControllerAxis toCheck) throws ControllerUnpluggedException {
ensureConnected();
return nativeCheckAxis(controllerPtr, toCheck.ordinal()) / AXIS_MAX_VAL;
}
private native int nativeCheckAxis(long controllerPtr, int axisIndex); /*
SDL_GameControllerUpdate();
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return SDL_GameControllerGetAxis(pad, (SDL_GameControllerAxis) axisIndex);
*/
/**
* Returns if passed axis is available on controller.
*
* @param toCheck The ControllerAxis to check
* @throws ControllerUnpluggedException If the controller is not connected
*/
public boolean isAxisAvailable(ControllerAxis toCheck) throws ControllerUnpluggedException {
ensureConnected();
return nativeAxisAvailable(controllerPtr, toCheck.ordinal());
}
private native boolean nativeAxisAvailable(long controllerPtr, int axisIndex); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return SDL_GameControllerHasAxis(pad, (SDL_GameControllerAxis) axisIndex);
*/
/**
* Returns the implementation dependent name of this controller.
*
* @return The the name of this controller
* @throws ControllerUnpluggedException If the controller is not connected
*/
public String getName() throws ControllerUnpluggedException {
ensureConnected();
String controllerName = nativeGetName(controllerPtr);
//Return a descriptive string instead of null if the attached controller does not have a name
if(controllerName == null) {
return "Unnamed Controller";
}
return controllerName;
}
private native String nativeGetName(long controllerPtr); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return env->NewStringUTF(SDL_GameControllerName(pad));
*/
/**
* Returns the instance ID of the current controller, which uniquely identifies
* the device from the time it is connected until it is disconnected.
*
* @return The instance ID of the current controller
* @throws ControllerUnpluggedException If the controller is not connected
*/
public int getDeviceInstanceID() throws ControllerUnpluggedException {
ensureConnected();
return nativeGetDeviceInstanceID(controllerPtr);
}
private native int nativeGetDeviceInstanceID(long controllerPtr); /*
SDL_Joystick* joystick = SDL_GameControllerGetJoystick((SDL_GameController*) controllerPtr);
return SDL_JoystickInstanceID(joystick);
*/
/**
* @return player index if set and supported, -1 otherwise
*/
public int getPlayerIndex() throws ControllerUnpluggedException {
ensureConnected();
return nativeGetPlayerIndex(controllerPtr);
}
private native int nativeGetPlayerIndex(long controllerPtr); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return SDL_GameControllerGetPlayerIndex(pad);
*/
/**
* Sets player index. At the time being, this doesn't seem to change the indication lights on
* a controller on Windows, Linux and Mac, but only an internal representation index.
* @param index index to set
*/
public void setPlayerIndex(int index) throws ControllerUnpluggedException {
ensureConnected();
nativeSetPlayerIndex(controllerPtr, index);
}
private native void nativeSetPlayerIndex(long controllerPtr, int index); /*
SDL_GameController* pad = (SDL_GameController*) controllerPtr;
return SDL_GameControllerSetPlayerIndex(pad, index);
*/
/**
* @return current power level of game controller, see {@link ControllerPowerLevel} enum values
* @throws ControllerUnpluggedException If the controller is not connected
*/
public ControllerPowerLevel getPowerLevel() throws ControllerUnpluggedException {
ensureConnected();
return ControllerPowerLevel.valueOf(nativeGetPowerLevel(controllerPtr));
}
private native int nativeGetPowerLevel(long controllerPtr); /*
SDL_Joystick* joystick = SDL_GameControllerGetJoystick((SDL_GameController*) controllerPtr);
return SDL_JoystickCurrentPowerLevel(joystick);
*/
/**
* Convenience method to throw an exception if the controller is not connected.
*/
private void ensureConnected() throws ControllerUnpluggedException {
if(!isConnected()) {
throw new ControllerUnpluggedException("Controller at index " + index + " is not connected!");
}
}
}