Mercurial > repos > blastem
view android/src/org/libsdl/app/SDLControllerManager.java @ 1863:d60f2d7c02a5
Differentiate between the full Sega mapper and the SRAM only one. Fixes crash on save state load for Phantasy Star IV and other games that use the same mapper
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 14 May 2019 23:20:35 -0700 |
parents | 78abbabfd58d |
children |
line wrap: on
line source
package org.libsdl.app; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Objects; 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(); 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 && !Objects.equals(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 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; } }