|
@@ -0,0 +1,427 @@
|
|
|
+package org.libsdl.app;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import android.app.*;
|
|
|
+import android.content.Context;
|
|
|
+import android.hardware.*;
|
|
|
+import android.os.*;
|
|
|
+import android.view.*;
|
|
|
+import android.util.Log;
|
|
|
+
|
|
|
+
|
|
|
+public class SDLControllerManager
|
|
|
+{
|
|
|
+
|
|
|
+ public static native int nativeSetupJNI();
|
|
|
+
|
|
|
+ public static native int nativeAddJoystick(int device_id, String name, String desc,
|
|
|
+ int is_accelerometer, int nbuttons,
|
|
|
+ int naxes, int nhats, int nballs);
|
|
|
+ public static native int nativeRemoveJoystick(int device_id);
|
|
|
+ public static native int nativeAddHaptic(int device_id, String name);
|
|
|
+ public static native int nativeRemoveHaptic(int device_id);
|
|
|
+ public static native int onNativePadDown(int device_id, int keycode);
|
|
|
+ public static native int onNativePadUp(int device_id, int keycode);
|
|
|
+ public static native void onNativeJoy(int device_id, int axis,
|
|
|
+ float value);
|
|
|
+ public static native void onNativeHat(int device_id, int hat_id,
|
|
|
+ int x, int y);
|
|
|
+
|
|
|
+ protected static SDLJoystickHandler mJoystickHandler;
|
|
|
+ protected static SDLHapticHandler mHapticHandler;
|
|
|
+
|
|
|
+ private static final String TAG = "SDLControllerManager";
|
|
|
+
|
|
|
+ public static void initialize() {
|
|
|
+ mJoystickHandler = null;
|
|
|
+ mHapticHandler = null;
|
|
|
+
|
|
|
+ SDLControllerManager.setup();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void setup() {
|
|
|
+ if (Build.VERSION.SDK_INT >= 16) {
|
|
|
+ mJoystickHandler = new SDLJoystickHandler_API16();
|
|
|
+ } else if (Build.VERSION.SDK_INT >= 12) {
|
|
|
+ mJoystickHandler = new SDLJoystickHandler_API12();
|
|
|
+ } else {
|
|
|
+ mJoystickHandler = new SDLJoystickHandler();
|
|
|
+ }
|
|
|
+ mHapticHandler = new SDLHapticHandler();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
|
|
+ public static boolean handleJoystickMotionEvent(MotionEvent event) {
|
|
|
+ return mJoystickHandler.handleMotionEvent(event);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method is called by SDL using JNI.
|
|
|
+ */
|
|
|
+ public static void pollInputDevices() {
|
|
|
+ mJoystickHandler.pollInputDevices();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method is called by SDL using JNI.
|
|
|
+ */
|
|
|
+ public static void pollHapticDevices() {
|
|
|
+ mHapticHandler.pollHapticDevices();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method is called by SDL using JNI.
|
|
|
+ */
|
|
|
+ public static void hapticRun(int device_id, int length) {
|
|
|
+ mHapticHandler.run(device_id, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if a given device is considered a possible SDL joystick
|
|
|
+ public static boolean isDeviceSDLJoystick(int deviceId) {
|
|
|
+ InputDevice device = InputDevice.getDevice(deviceId);
|
|
|
+ // We cannot use InputDevice.isVirtual before API 16, so let's accept
|
|
|
+ // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
|
|
+ if ((device == null) || (deviceId < 0)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int sources = device.getSources();
|
|
|
+
|
|
|
+ if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
|
|
|
+ Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
|
|
|
+ }
|
|
|
+ if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
|
|
+ Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
|
|
+ }
|
|
|
+ if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
|
|
+ Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
|
|
+ }
|
|
|
+
|
|
|
+ return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
|
|
|
+ ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
|
|
+ ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
|
|
|
+class SDLJoystickHandler {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Handles given MotionEvent.
|
|
|
+ * @param event the event to be handled.
|
|
|
+ * @return if given event was processed.
|
|
|
+ */
|
|
|
+ public boolean handleMotionEvent(MotionEvent event) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Handles adding and removing of input devices.
|
|
|
+ */
|
|
|
+ public void pollInputDevices() {
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Actual joystick functionality available for API >= 12 devices */
|
|
|
+class SDLJoystickHandler_API12 extends SDLJoystickHandler {
|
|
|
+
|
|
|
+ static class SDLJoystick {
|
|
|
+ public int device_id;
|
|
|
+ public String name;
|
|
|
+ public String desc;
|
|
|
+ public ArrayList<InputDevice.MotionRange> axes;
|
|
|
+ public ArrayList<InputDevice.MotionRange> hats;
|
|
|
+ }
|
|
|
+ static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
|
|
+ @Override
|
|
|
+ public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
|
|
+ return arg0.getAxis() - arg1.getAxis();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private ArrayList<SDLJoystick> mJoysticks;
|
|
|
+
|
|
|
+ public SDLJoystickHandler_API12() {
|
|
|
+
|
|
|
+ mJoysticks = new ArrayList<SDLJoystick>();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void pollInputDevices() {
|
|
|
+ int[] deviceIds = InputDevice.getDeviceIds();
|
|
|
+ // It helps processing the device ids in reverse order
|
|
|
+ // For example, in the case of the XBox 360 wireless dongle,
|
|
|
+ // so the first controller seen by SDL matches what the receiver
|
|
|
+ // considers to be the first controller
|
|
|
+
|
|
|
+ for(int i=deviceIds.length-1; i>-1; i--) {
|
|
|
+ SDLJoystick joystick = getJoystick(deviceIds[i]);
|
|
|
+ if (joystick == null) {
|
|
|
+ joystick = new SDLJoystick();
|
|
|
+ InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
|
|
|
+ if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
|
|
|
+ joystick.device_id = deviceIds[i];
|
|
|
+ joystick.name = joystickDevice.getName();
|
|
|
+ joystick.desc = getJoystickDescriptor(joystickDevice);
|
|
|
+ joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
|
|
+ joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
|
|
+
|
|
|
+ List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
|
|
+ Collections.sort(ranges, new RangeComparator());
|
|
|
+ for (InputDevice.MotionRange range : ranges ) {
|
|
|
+ if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
|
|
+ if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
|
|
|
+ range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
|
|
+ joystick.hats.add(range);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ joystick.axes.add(range);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mJoysticks.add(joystick);
|
|
|
+ SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
|
|
|
+ joystick.axes.size(), joystick.hats.size()/2, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check removed devices */
|
|
|
+ ArrayList<Integer> removedDevices = new ArrayList<Integer>();
|
|
|
+ for(int i=0; i < mJoysticks.size(); i++) {
|
|
|
+ int device_id = mJoysticks.get(i).device_id;
|
|
|
+ int j;
|
|
|
+ for (j=0; j < deviceIds.length; j++) {
|
|
|
+ if (device_id == deviceIds[j]) break;
|
|
|
+ }
|
|
|
+ if (j == deviceIds.length) {
|
|
|
+ removedDevices.add(Integer.valueOf(device_id));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for(int i=0; i < removedDevices.size(); i++) {
|
|
|
+ int device_id = removedDevices.get(i).intValue();
|
|
|
+ SDLControllerManager.nativeRemoveJoystick(device_id);
|
|
|
+ for (int j=0; j < mJoysticks.size(); j++) {
|
|
|
+ if (mJoysticks.get(j).device_id == device_id) {
|
|
|
+ mJoysticks.remove(j);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected SDLJoystick getJoystick(int device_id) {
|
|
|
+ for(int i=0; i < mJoysticks.size(); i++) {
|
|
|
+ if (mJoysticks.get(i).device_id == device_id) {
|
|
|
+ return mJoysticks.get(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean handleMotionEvent(MotionEvent event) {
|
|
|
+ if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
|
|
|
+ int actionPointerIndex = event.getActionIndex();
|
|
|
+ int action = event.getActionMasked();
|
|
|
+ switch(action) {
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+ SDLJoystick joystick = getJoystick(event.getDeviceId());
|
|
|
+ if ( joystick != null ) {
|
|
|
+ for (int i = 0; i < joystick.axes.size(); i++) {
|
|
|
+ InputDevice.MotionRange range = joystick.axes.get(i);
|
|
|
+ /* Normalize the value to -1...1 */
|
|
|
+ float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
|
|
|
+ SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
|
|
|
+ }
|
|
|
+ for (int i = 0; i < joystick.hats.size(); i+=2) {
|
|
|
+ int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
|
|
|
+ int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
|
|
|
+ SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getJoystickDescriptor(InputDevice joystickDevice) {
|
|
|
+ return joystickDevice.getName();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getJoystickDescriptor(InputDevice joystickDevice) {
|
|
|
+ String desc = joystickDevice.getDescriptor();
|
|
|
+
|
|
|
+ if (desc != null && desc != "") {
|
|
|
+ return desc;
|
|
|
+ }
|
|
|
+
|
|
|
+ return super.getJoystickDescriptor(joystickDevice);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class SDLHapticHandler {
|
|
|
+
|
|
|
+ class SDLHaptic {
|
|
|
+ public int device_id;
|
|
|
+ public String name;
|
|
|
+ public Vibrator vib;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ArrayList<SDLHaptic> mHaptics;
|
|
|
+
|
|
|
+ public SDLHapticHandler() {
|
|
|
+ mHaptics = new ArrayList<SDLHaptic>();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void run(int device_id, int length) {
|
|
|
+ SDLHaptic haptic = getHaptic(device_id);
|
|
|
+ if (haptic != null) {
|
|
|
+ haptic.vib.vibrate (length);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void pollHapticDevices() {
|
|
|
+
|
|
|
+ final int deviceId_VIBRATOR_SERVICE = 999999;
|
|
|
+ boolean hasVibrator = false;
|
|
|
+
|
|
|
+ int[] deviceIds = InputDevice.getDeviceIds();
|
|
|
+ // It helps processing the device ids in reverse order
|
|
|
+ // For example, in the case of the XBox 360 wireless dongle,
|
|
|
+ // so the first controller seen by SDL matches what the receiver
|
|
|
+ // considers to be the first controller
|
|
|
+
|
|
|
+ for(int i=deviceIds.length-1; i>-1; i--) {
|
|
|
+ SDLHaptic haptic = getHaptic(deviceIds[i]);
|
|
|
+ if (haptic == null) {
|
|
|
+ InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
|
|
+ Vibrator vib = device.getVibrator();
|
|
|
+ if (vib.hasVibrator()) {
|
|
|
+ haptic = new SDLHaptic();
|
|
|
+ haptic.device_id = deviceIds[i];
|
|
|
+ haptic.name = device.getName();
|
|
|
+ haptic.vib = vib;
|
|
|
+ mHaptics.add(haptic);
|
|
|
+ SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check VIBRATOR_SERVICE */
|
|
|
+ {
|
|
|
+ Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
|
|
+ if (vib != null && vib.hasVibrator()) {
|
|
|
+ hasVibrator = true;
|
|
|
+ SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
|
|
+ if (haptic == null) {
|
|
|
+ haptic = new SDLHaptic();
|
|
|
+ haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
|
|
+ haptic.name = "VIBRATOR_SERVICE";
|
|
|
+ haptic.vib = vib;
|
|
|
+ mHaptics.add(haptic);
|
|
|
+ SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check removed devices */
|
|
|
+ ArrayList<Integer> removedDevices = new ArrayList<Integer>();
|
|
|
+ for(int i=0; i < mHaptics.size(); i++) {
|
|
|
+ int device_id = mHaptics.get(i).device_id;
|
|
|
+ int j;
|
|
|
+ for (j=0; j < deviceIds.length; j++) {
|
|
|
+ if (device_id == deviceIds[j]) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (device_id == deviceId_VIBRATOR_SERVICE && hasVibrator) {
|
|
|
+ // don't remove the vibrator if it is still present
|
|
|
+ } else if (j == deviceIds.length) {
|
|
|
+ removedDevices.add(device_id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for(int i=0; i < removedDevices.size(); i++) {
|
|
|
+ int device_id = removedDevices.get(i);
|
|
|
+ SDLControllerManager.nativeRemoveHaptic(device_id);
|
|
|
+ for (int j=0; j < mHaptics.size(); j++) {
|
|
|
+ if (mHaptics.get(j).device_id == device_id) {
|
|
|
+ mHaptics.remove(j);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected SDLHaptic getHaptic(int device_id) {
|
|
|
+ for(int i=0; i < mHaptics.size(); i++) {
|
|
|
+ if (mHaptics.get(i).device_id == device_id) {
|
|
|
+ return mHaptics.get(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
|
|
|
+ // Generic Motion (mouse hover, joystick...) events go here
|
|
|
+ @Override
|
|
|
+ public boolean onGenericMotion(View v, MotionEvent event) {
|
|
|
+ float x, y;
|
|
|
+ int action;
|
|
|
+
|
|
|
+ switch ( event.getSource() ) {
|
|
|
+ case InputDevice.SOURCE_JOYSTICK:
|
|
|
+ case InputDevice.SOURCE_GAMEPAD:
|
|
|
+ case InputDevice.SOURCE_DPAD:
|
|
|
+ return SDLControllerManager.handleJoystickMotionEvent(event);
|
|
|
+
|
|
|
+ case InputDevice.SOURCE_MOUSE:
|
|
|
+ if (!SDLActivity.mSeparateMouseAndTouch) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ action = event.getActionMasked();
|
|
|
+ switch (action) {
|
|
|
+ case MotionEvent.ACTION_SCROLL:
|
|
|
+ x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
|
|
+ y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
|
|
+ SDLActivity.onNativeMouse(0, action, x, y);
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case MotionEvent.ACTION_HOVER_MOVE:
|
|
|
+ x = event.getX(0);
|
|
|
+ y = event.getY(0);
|
|
|
+
|
|
|
+ SDLActivity.onNativeMouse(0, action, x, y);
|
|
|
+ return true;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Event was not managed
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|