| package org.libsdl.app; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| import android.content.Context; |
| 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(); |
| |
| /* This is called for every button press, so let's not spam the logs */ |
| /** |
| 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.isEmpty()) { |
| 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 hasVibratorService = 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 |
| |
| if (Build.VERSION.SDK_INT >= 16) |
| { |
| 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) { |
| if (Build.VERSION.SDK_INT >= 11) { |
| hasVibratorService = vib.hasVibrator(); |
| } else { |
| hasVibratorService = true; |
| } |
| |
| if (hasVibratorService) { |
| 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 && hasVibratorService) { |
| // 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; |
| } |
| } |
| |