1 package org
.libsdl
.app
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Collections
;
5 import java
.util
.Comparator
;
7 import java
.util
.Objects
;
9 import android
.content
.Context
;
11 import android
.view
.*;
12 import android
.util
.Log
;
15 public class SDLControllerManager
18 public static native int nativeSetupJNI();
20 public static native int nativeAddJoystick(int device_id
, String name
, String desc
,
21 int is_accelerometer
, int nbuttons
,
22 int naxes
, int nhats
, int nballs
);
23 public static native int nativeRemoveJoystick(int device_id
);
24 public static native int nativeAddHaptic(int device_id
, String name
);
25 public static native int nativeRemoveHaptic(int device_id
);
26 public static native int onNativePadDown(int device_id
, int keycode
);
27 public static native int onNativePadUp(int device_id
, int keycode
);
28 public static native void onNativeJoy(int device_id
, int axis
,
30 public static native void onNativeHat(int device_id
, int hat_id
,
33 protected static SDLJoystickHandler mJoystickHandler
;
34 protected static SDLHapticHandler mHapticHandler
;
36 private static final String TAG
= "SDLControllerManager";
38 public static void initialize() {
39 mJoystickHandler
= null;
40 mHapticHandler
= null;
42 SDLControllerManager
.setup();
45 public static void setup() {
46 if (Build
.VERSION
.SDK_INT
>= 16) {
47 mJoystickHandler
= new SDLJoystickHandler_API16();
48 } else if (Build
.VERSION
.SDK_INT
>= 12) {
49 mJoystickHandler
= new SDLJoystickHandler_API12();
51 mJoystickHandler
= new SDLJoystickHandler();
53 mHapticHandler
= new SDLHapticHandler();
56 // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
57 public static boolean handleJoystickMotionEvent(MotionEvent event
) {
58 return mJoystickHandler
.handleMotionEvent(event
);
62 * This method is called by SDL using JNI.
64 public static void pollInputDevices() {
65 mJoystickHandler
.pollInputDevices();
69 * This method is called by SDL using JNI.
71 public static void pollHapticDevices() {
72 mHapticHandler
.pollHapticDevices();
76 * This method is called by SDL using JNI.
78 public static void hapticRun(int device_id
, int length
) {
79 mHapticHandler
.run(device_id
, length
);
82 // Check if a given device is considered a possible SDL joystick
83 public static boolean isDeviceSDLJoystick(int deviceId
) {
84 InputDevice device
= InputDevice
.getDevice(deviceId
);
85 // We cannot use InputDevice.isVirtual before API 16, so let's accept
86 // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
87 if ((device
== null) || (deviceId
< 0)) {
90 int sources
= device
.getSources();
92 if ((sources
& InputDevice
.SOURCE_CLASS_JOYSTICK
) == InputDevice
.SOURCE_CLASS_JOYSTICK
) {
93 Log
.v(TAG
, "Input device " + device
.getName() + " is a joystick.");
95 if ((sources
& InputDevice
.SOURCE_DPAD
) == InputDevice
.SOURCE_DPAD
) {
96 Log
.v(TAG
, "Input device " + device
.getName() + " is a dpad.");
98 if ((sources
& InputDevice
.SOURCE_GAMEPAD
) == InputDevice
.SOURCE_GAMEPAD
) {
99 Log
.v(TAG
, "Input device " + device
.getName() + " is a gamepad.");
102 return (((sources
& InputDevice
.SOURCE_CLASS_JOYSTICK
) == InputDevice
.SOURCE_CLASS_JOYSTICK
) ||
103 ((sources
& InputDevice
.SOURCE_DPAD
) == InputDevice
.SOURCE_DPAD
) ||
104 ((sources
& InputDevice
.SOURCE_GAMEPAD
) == InputDevice
.SOURCE_GAMEPAD
)
110 /* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
111 class SDLJoystickHandler
{
114 * Handles given MotionEvent.
115 * @param event the event to be handled.
116 * @return if given event was processed.
118 public boolean handleMotionEvent(MotionEvent event
) {
123 * Handles adding and removing of input devices.
125 public void pollInputDevices() {
129 /* Actual joystick functionality available for API >= 12 devices */
130 class SDLJoystickHandler_API12
extends SDLJoystickHandler
{
132 static class SDLJoystick
{
133 public int device_id
;
136 public ArrayList
<InputDevice
.MotionRange
> axes
;
137 public ArrayList
<InputDevice
.MotionRange
> hats
;
139 static class RangeComparator
implements Comparator
<InputDevice
.MotionRange
> {
141 public int compare(InputDevice
.MotionRange arg0
, InputDevice
.MotionRange arg1
) {
142 return arg0
.getAxis() - arg1
.getAxis();
146 private ArrayList
<SDLJoystick
> mJoysticks
;
148 public SDLJoystickHandler_API12() {
150 mJoysticks
= new ArrayList
<SDLJoystick
>();
154 public void pollInputDevices() {
155 int[] deviceIds
= InputDevice
.getDeviceIds();
156 // It helps processing the device ids in reverse order
157 // For example, in the case of the XBox 360 wireless dongle,
158 // so the first controller seen by SDL matches what the receiver
159 // considers to be the first controller
161 for(int i
=deviceIds
.length
-1; i
>-1; i
--) {
162 SDLJoystick joystick
= getJoystick(deviceIds
[i
]);
163 if (joystick
== null) {
164 joystick
= new SDLJoystick();
165 InputDevice joystickDevice
= InputDevice
.getDevice(deviceIds
[i
]);
166 if (SDLControllerManager
.isDeviceSDLJoystick(deviceIds
[i
])) {
167 joystick
.device_id
= deviceIds
[i
];
168 joystick
.name
= joystickDevice
.getName();
169 joystick
.desc
= getJoystickDescriptor(joystickDevice
);
170 joystick
.axes
= new ArrayList
<InputDevice
.MotionRange
>();
171 joystick
.hats
= new ArrayList
<InputDevice
.MotionRange
>();
173 List
<InputDevice
.MotionRange
> ranges
= joystickDevice
.getMotionRanges();
174 Collections
.sort(ranges
, new RangeComparator());
175 for (InputDevice
.MotionRange range
: ranges
) {
176 if ((range
.getSource() & InputDevice
.SOURCE_CLASS_JOYSTICK
) != 0) {
177 if (range
.getAxis() == MotionEvent
.AXIS_HAT_X
||
178 range
.getAxis() == MotionEvent
.AXIS_HAT_Y
) {
179 joystick
.hats
.add(range
);
182 joystick
.axes
.add(range
);
187 mJoysticks
.add(joystick
);
188 SDLControllerManager
.nativeAddJoystick(joystick
.device_id
, joystick
.name
, joystick
.desc
, 0, -1,
189 joystick
.axes
.size(), joystick
.hats
.size()/2, 0);
194 /* Check removed devices */
195 ArrayList
<Integer
> removedDevices
= new ArrayList
<Integer
>();
196 for(int i
=0; i
< mJoysticks
.size(); i
++) {
197 int device_id
= mJoysticks
.get(i
).device_id
;
199 for (j
=0; j
< deviceIds
.length
; j
++) {
200 if (device_id
== deviceIds
[j
]) break;
202 if (j
== deviceIds
.length
) {
203 removedDevices
.add(Integer
.valueOf(device_id
));
207 for(int i
=0; i
< removedDevices
.size(); i
++) {
208 int device_id
= removedDevices
.get(i
).intValue();
209 SDLControllerManager
.nativeRemoveJoystick(device_id
);
210 for (int j
=0; j
< mJoysticks
.size(); j
++) {
211 if (mJoysticks
.get(j
).device_id
== device_id
) {
212 mJoysticks
.remove(j
);
219 protected SDLJoystick
getJoystick(int device_id
) {
220 for(int i
=0; i
< mJoysticks
.size(); i
++) {
221 if (mJoysticks
.get(i
).device_id
== device_id
) {
222 return mJoysticks
.get(i
);
229 public boolean handleMotionEvent(MotionEvent event
) {
230 if ((event
.getSource() & InputDevice
.SOURCE_JOYSTICK
) != 0) {
231 int actionPointerIndex
= event
.getActionIndex();
232 int action
= event
.getActionMasked();
234 case MotionEvent
.ACTION_MOVE
:
235 SDLJoystick joystick
= getJoystick(event
.getDeviceId());
236 if ( joystick
!= null ) {
237 for (int i
= 0; i
< joystick
.axes
.size(); i
++) {
238 InputDevice
.MotionRange range
= joystick
.axes
.get(i
);
239 /* Normalize the value to -1...1 */
240 float value
= ( event
.getAxisValue( range
.getAxis(), actionPointerIndex
) - range
.getMin() ) / range
.getRange() * 2.0f - 1.0f;
241 SDLControllerManager
.onNativeJoy(joystick
.device_id
, i
, value
);
243 for (int i
= 0; i
< joystick
.hats
.size(); i
+=2) {
244 int hatX
= Math
.round(event
.getAxisValue( joystick
.hats
.get(i
).getAxis(), actionPointerIndex
) );
245 int hatY
= Math
.round(event
.getAxisValue( joystick
.hats
.get(i
+1).getAxis(), actionPointerIndex
) );
246 SDLControllerManager
.onNativeHat(joystick
.device_id
, i
/2, hatX
, hatY
);
257 public String
getJoystickDescriptor(InputDevice joystickDevice
) {
258 return joystickDevice
.getName();
263 class SDLJoystickHandler_API16
extends SDLJoystickHandler_API12
{
266 public String
getJoystickDescriptor(InputDevice joystickDevice
) {
267 String desc
= joystickDevice
.getDescriptor();
269 if (desc
!= null && !Objects
.equals(desc
, "")) {
273 return super.getJoystickDescriptor(joystickDevice
);
277 class SDLHapticHandler
{
280 public int device_id
;
285 private ArrayList
<SDLHaptic
> mHaptics
;
287 public SDLHapticHandler() {
288 mHaptics
= new ArrayList
<SDLHaptic
>();
291 public void run(int device_id
, int length
) {
292 SDLHaptic haptic
= getHaptic(device_id
);
293 if (haptic
!= null) {
294 haptic
.vib
.vibrate (length
);
298 public void pollHapticDevices() {
300 final int deviceId_VIBRATOR_SERVICE
= 999999;
301 boolean hasVibratorService
= false;
303 int[] deviceIds
= InputDevice
.getDeviceIds();
304 // It helps processing the device ids in reverse order
305 // For example, in the case of the XBox 360 wireless dongle,
306 // so the first controller seen by SDL matches what the receiver
307 // considers to be the first controller
309 if (Build
.VERSION
.SDK_INT
>= 16)
311 for (int i
= deviceIds
.length
- 1; i
> -1; i
--) {
312 SDLHaptic haptic
= getHaptic(deviceIds
[i
]);
313 if (haptic
== null) {
314 InputDevice device
= InputDevice
.getDevice(deviceIds
[i
]);
315 Vibrator vib
= device
.getVibrator();
316 if (vib
.hasVibrator()) {
317 haptic
= new SDLHaptic();
318 haptic
.device_id
= deviceIds
[i
];
319 haptic
.name
= device
.getName();
321 mHaptics
.add(haptic
);
322 SDLControllerManager
.nativeAddHaptic(haptic
.device_id
, haptic
.name
);
328 /* Check VIBRATOR_SERVICE */
329 Vibrator vib
= (Vibrator
) SDL
.getContext().getSystemService(Context
.VIBRATOR_SERVICE
);
331 if (Build
.VERSION
.SDK_INT
>= 11) {
332 hasVibratorService
= vib
.hasVibrator();
334 hasVibratorService
= true;
337 if (hasVibratorService
) {
338 SDLHaptic haptic
= getHaptic(deviceId_VIBRATOR_SERVICE
);
339 if (haptic
== null) {
340 haptic
= new SDLHaptic();
341 haptic
.device_id
= deviceId_VIBRATOR_SERVICE
;
342 haptic
.name
= "VIBRATOR_SERVICE";
344 mHaptics
.add(haptic
);
345 SDLControllerManager
.nativeAddHaptic(haptic
.device_id
, haptic
.name
);
350 /* Check removed devices */
351 ArrayList
<Integer
> removedDevices
= new ArrayList
<Integer
>();
352 for(int i
=0; i
< mHaptics
.size(); i
++) {
353 int device_id
= mHaptics
.get(i
).device_id
;
355 for (j
=0; j
< deviceIds
.length
; j
++) {
356 if (device_id
== deviceIds
[j
]) break;
359 if (device_id
== deviceId_VIBRATOR_SERVICE
&& hasVibratorService
) {
360 // don't remove the vibrator if it is still present
361 } else if (j
== deviceIds
.length
) {
362 removedDevices
.add(device_id
);
366 for(int i
=0; i
< removedDevices
.size(); i
++) {
367 int device_id
= removedDevices
.get(i
);
368 SDLControllerManager
.nativeRemoveHaptic(device_id
);
369 for (int j
=0; j
< mHaptics
.size(); j
++) {
370 if (mHaptics
.get(j
).device_id
== device_id
) {
378 protected SDLHaptic
getHaptic(int device_id
) {
379 for(int i
=0; i
< mHaptics
.size(); i
++) {
380 if (mHaptics
.get(i
).device_id
== device_id
) {
381 return mHaptics
.get(i
);
388 class SDLGenericMotionListener_API12
implements View
.OnGenericMotionListener
{
389 // Generic Motion (mouse hover, joystick...) events go here
391 public boolean onGenericMotion(View v
, MotionEvent event
) {
395 switch ( event
.getSource() ) {
396 case InputDevice
.SOURCE_JOYSTICK
:
397 case InputDevice
.SOURCE_GAMEPAD
:
398 case InputDevice
.SOURCE_DPAD
:
399 return SDLControllerManager
.handleJoystickMotionEvent(event
);
401 case InputDevice
.SOURCE_MOUSE
:
402 if (!SDLActivity
.mSeparateMouseAndTouch
) {
405 action
= event
.getActionMasked();
407 case MotionEvent
.ACTION_SCROLL
:
408 x
= event
.getAxisValue(MotionEvent
.AXIS_HSCROLL
, 0);
409 y
= event
.getAxisValue(MotionEvent
.AXIS_VSCROLL
, 0);
410 SDLActivity
.onNativeMouse(0, action
, x
, y
);
413 case MotionEvent
.ACTION_HOVER_MOVE
:
417 SDLActivity
.onNativeMouse(0, action
, x
, y
);
429 // Event was not managed