Forráskód Böngészése

Add support for joystick motion sensors on Android

Nintorch 1 hete
szülő
commit
c699512adc

+ 76 - 2
android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java

@@ -10,6 +10,10 @@ import android.hardware.lights.Light;
 import android.hardware.lights.LightsRequest;
 import android.hardware.lights.LightsManager;
 import android.hardware.lights.LightState;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.graphics.Color;
 import android.os.Build;
 import android.os.VibrationEffect;
@@ -30,7 +34,8 @@ public class SDLControllerManager
     static native void nativeAddJoystick(int device_id, String name, String desc,
                                                 int vendor_id, int product_id,
                                                 int button_mask,
-                                                int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led);
+                                                int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led,
+                                                boolean has_accelerometer, boolean has_gyroscope);
     static native void nativeRemoveJoystick(int device_id);
     static native void nativeAddHaptic(int device_id, String name);
     static native void nativeRemoveHaptic(int device_id);
@@ -40,6 +45,7 @@ public class SDLControllerManager
                                           float value);
     static native void onNativeHat(int device_id, int hat_id,
                                           int x, int y);
+    static native void onNativeJoySensor(int device_id, int sensor_type, long sensor_timestamp, float x, float y, float z);
 
     protected static SDLJoystickHandler mJoystickHandler;
     protected static SDLHapticHandler mHapticHandler;
@@ -81,6 +87,13 @@ public class SDLControllerManager
         mJoystickHandler.setLED(device_id, red, green, blue);
     }
 
+    /**
+     * This method is called by SDL using JNI.
+     */
+    static void joystickSetSensorsEnabled(int device_id, boolean enabled) {
+        mJoystickHandler.setSensorsEnabled(device_id, enabled);
+    }
+
     /**
      * This method is called by SDL using JNI.
      */
@@ -153,6 +166,10 @@ class SDLJoystickHandler {
         ArrayList<InputDevice.MotionRange> hats;
         ArrayList<Light> lights;
         LightsManager.LightsSession lightsSession;
+        SensorManager sensorManager;
+        SDLJoySensorListener sensorListener;
+        Sensor accelerometerSensor;
+        Sensor gyroscopeSensor;
     }
     static class RangeComparator implements Comparator<InputDevice.MotionRange> {
         @Override
@@ -241,6 +258,8 @@ class SDLJoystickHandler {
 
                     boolean can_rumble = false;
                     boolean has_rgb_led = false;
+                    boolean has_accelerometer = false;
+                    boolean has_gyroscope = false;
                     if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
                         VibratorManager vibratorManager = joystickDevice.getVibratorManager();
                         int[] vibrators = vibratorManager.getVibratorIds();
@@ -258,12 +277,26 @@ class SDLJoystickHandler {
                             joystick.lightsSession = lightsManager.openSession();
                             has_rgb_led = true;
                         }
+                        SensorManager sensorManager = joystickDevice.getSensorManager();
+                        if (sensorManager != null) {
+                            joystick.sensorManager = sensorManager;
+                            joystick.sensorListener = new SDLJoySensorListener(joystick.device_id);
+                            joystick.accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+                            if (joystick.accelerometerSensor != null) {
+                                has_accelerometer = true;
+                            }
+                            joystick.gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
+                            if (joystick.gyroscopeSensor != null) {
+                                has_gyroscope = true;
+                            }
+                        }
                     }
 
                     mJoysticks.add(joystick);
                     SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
                             getVendorId(joystickDevice), getProductId(joystickDevice),
-                            getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led);
+                            getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led,
+                            has_accelerometer, has_gyroscope);
                 }
             }
         }
@@ -508,6 +541,31 @@ class SDLJoystickHandler {
         }
         joystick.lightsSession.requestLights(lightsRequest.build());
     }
+
+    void setSensorsEnabled(int device_id, boolean enabled) {
+        if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
+            return;
+        }
+        SDLJoystick joystick = getJoystick(device_id);
+        if (joystick == null || joystick.sensorManager == null) {
+            return;
+        }
+        if (enabled) {
+            if (joystick.accelerometerSensor != null) {
+                joystick.sensorManager.registerListener(joystick.sensorListener, joystick.accelerometerSensor, SensorManager.SENSOR_DELAY_GAME, null);
+            }
+            if (joystick.gyroscopeSensor != null) {
+                joystick.sensorManager.registerListener(joystick.sensorListener, joystick.gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME, null);
+            }
+        } else {
+            if (joystick.accelerometerSensor != null) {
+                joystick.sensorManager.unregisterListener(joystick.sensorListener, joystick.accelerometerSensor);
+            }
+            if (joystick.gyroscopeSensor != null) {
+                joystick.sensorManager.unregisterListener(joystick.sensorListener, joystick.gyroscopeSensor);
+            }
+        }
+    }
 }
 
 class SDLHapticHandler_API31 extends SDLHapticHandler {
@@ -933,3 +991,19 @@ class SDLGenericMotionListener_API29 extends SDLGenericMotionListener_API26 {
         return penDevice.isExternal() ? SDL_PEN_DEVICE_TYPE_INDIRECT : SDL_PEN_DEVICE_TYPE_DIRECT;
     }
 }
+
+class SDLJoySensorListener implements SensorEventListener {
+    int device_id;
+
+    public SDLJoySensorListener(int device_id) {
+        this.device_id = device_id;
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        SDLControllerManager.onNativeJoySensor(device_id, event.sensor.getType(), event.timestamp, event.values[0], event.values[1], event.values[2]);
+    }
+}

+ 32 - 5
src/core/android/SDL_android.c

@@ -312,10 +312,15 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
     JNIEnv *env, jclass jcls,
     jint device_id, jint hat_id, jint x, jint y);
 
+JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoySensor)(
+    JNIEnv *env, jclass jcls,
+    jint device_id, jint sensor_type, jlong sensor_timestamp, jfloat x, jfloat y, jfloat z);
+
 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
     JNIEnv *env, jclass jcls,
     jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
-    jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led);
+    jint button_mask, jint naxes, jint axis_mask, jint nhats,
+    jboolean can_rumble, jboolean has_rgb_led, jboolean has_accelerometer, jboolean has_gyroscope);
 
 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
     JNIEnv *env, jclass jcls,
@@ -335,7 +340,8 @@ static JNINativeMethod SDLControllerManager_tab[] = {
     { "onNativePadUp", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) },
     { "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) },
     { "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) },
-    { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
+    { "onNativeJoySensor", "(IIJFFF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoySensor) },
+    { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZZZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
     { "nativeRemoveJoystick", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) },
     { "nativeAddHaptic", "(ILjava/lang/String;)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) },
     { "nativeRemoveHaptic", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) }
@@ -408,6 +414,7 @@ static jclass mControllerManagerClass;
 // method signatures
 static jmethodID midPollInputDevices;
 static jmethodID midJoystickSetLED;
+static jmethodID midJoystickSetSensorsEnabled;
 static jmethodID midPollHapticDevices;
 static jmethodID midHapticRun;
 static jmethodID midHapticRumble;
@@ -756,6 +763,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env
                                                     "pollInputDevices", "()V");
     midJoystickSetLED = (*env)->GetStaticMethodID(env, mControllerManagerClass,
                                               "joystickSetLED", "(IIII)V");
+    midJoystickSetSensorsEnabled = (*env)->GetStaticMethodID(env, mControllerManagerClass,
+                                              "joystickSetSensorsEnabled", "(IZ)V");
     midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
                                                      "pollHapticDevices", "()V");
     midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
@@ -765,7 +774,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env
     midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
                                               "hapticStop", "(I)V");
 
-    if (!midPollInputDevices || !midJoystickSetLED || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) {
+    if (!midPollInputDevices || !midJoystickSetLED || !midJoystickSetSensorsEnabled || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
     }
 
@@ -1191,17 +1200,29 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
 #endif // SDL_JOYSTICK_ANDROID
 }
 
+JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoySensor)(
+    JNIEnv *env, jclass jcls,
+    jint device_id, jint sensor_type, jlong sensor_timestamp, jfloat x, jfloat y, jfloat z)
+{
+#ifdef SDL_JOYSTICK_ANDROID
+    // In Java there's no Uint64 type, so pass Sint64 as if it was Uint64.
+    Android_OnJoySensor(device_id, sensor_type, sensor_timestamp, x, y, z);
+#endif // SDL_JOYSTICK_ANDROID
+}
+
 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
     JNIEnv *env, jclass jcls,
     jint device_id, jstring device_name, jstring device_desc,
     jint vendor_id, jint product_id,
-    jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led)
+    jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led,
+    jboolean has_accelerometer, jboolean has_gyroscope)
 {
 #ifdef SDL_JOYSTICK_ANDROID
     const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
     const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
 
-    Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble, has_rgb_led);
+    Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats,
+        can_rumble, has_rgb_led, has_accelerometer, has_gyroscope);
 
     (*env)->ReleaseStringUTFChars(env, device_name, name);
     (*env)->ReleaseStringUTFChars(env, device_desc, desc);
@@ -2626,6 +2647,12 @@ void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue)
     (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetLED, device_id, red, green, blue);
 }
 
+void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetSensorsEnabled, device_id, (enabled == 1));
+}
+
 void Android_JNI_PollHapticDevices(void)
 {
     JNIEnv *env = Android_JNI_GetEnv();

+ 1 - 0
src/core/android/SDL_android.h

@@ -105,6 +105,7 @@ int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seco
 // Joystick support
 void Android_JNI_PollInputDevices(void);
 void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue);
+void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled);
 
 // Haptic support
 void Android_JNI_PollHapticDevices(void);

+ 49 - 2
src/joystick/android/SDL_sysjoystick.c

@@ -306,7 +306,37 @@ bool Android_OnHat(int device_id, int hat_id, int x, int y)
     return false;
 }
 
-void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats, bool can_rumble, bool has_rgb_led)
+void Android_OnJoySensor(int device_id, int sensor_type, Uint64 sensor_timestamp, float x, float y, float z)
+{
+    Uint64 timestamp = SDL_GetTicksNS();
+    SDL_joylist_item *item;
+    SDL_SensorType sensor;
+    float data[3];
+
+    if (sensor_type == 1) { // Sensor.TYPE_ACCELEROMETER
+        sensor = SDL_SENSOR_ACCEL;
+    } else if (sensor_type == 4) { // Sensor.TYPE_GYROSCOPE
+        sensor = SDL_SENSOR_GYRO;
+    } else {
+        // Unsupported sensor
+        return;
+    }
+
+    // The axes of sensor events and their signs are the same as SDL's, so no conversion required
+    data[0] = x;
+    data[1] = y;
+    data[2] = z;
+
+    SDL_LockJoysticks();
+    item = JoystickByDeviceId(device_id);
+    if (item && item->joystick) {
+        SDL_SendJoystickSensor(timestamp, item->joystick, sensor, sensor_timestamp, data, 3);
+    }
+    SDL_UnlockJoysticks();
+}
+
+void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats,
+    bool can_rumble, bool has_rgb_led, bool has_accelerometer, bool has_gyroscope)
 {
     SDL_joylist_item *item;
     SDL_GUID guid;
@@ -382,6 +412,8 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int
     item->nhats = nhats;
     item->can_rumble = can_rumble;
     item->has_rgb_led = has_rgb_led;
+    item->has_accelerometer = has_accelerometer;
+    item->has_gyroscope = has_accelerometer;
     item->device_instance = SDL_GetNextObjectID();
     if (!SDL_joylist_tail) {
         SDL_joylist = SDL_joylist_tail = item;
@@ -587,6 +619,13 @@ static bool ANDROID_JoystickOpen(SDL_Joystick *joystick, int device_index)
         SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true);
     }
 
+    if (item->has_accelerometer) {
+        SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
+    }
+    if (item->has_gyroscope) {
+        SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
+    }
+
     return true;
 }
 
@@ -631,7 +670,15 @@ static bool ANDROID_JoystickSendEffect(SDL_Joystick *joystick, const void *data,
 
 static bool ANDROID_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
 {
-    return SDL_Unsupported();
+    SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata;
+    if (!item) {
+        return SDL_SetError("SetSensorsEnabled failed, device disconnected");
+    }
+    if (!item->has_accelerometer && !item->has_gyroscope) {
+        return SDL_Unsupported();
+    }
+    Android_JNI_JoystickSetSensorsEnabled(item->device_id, enabled);
+    return true;
 }
 
 static void ANDROID_JoystickUpdate(SDL_Joystick *joystick)

+ 5 - 1
src/joystick/android/SDL_sysjoystick_c.h

@@ -32,7 +32,9 @@ extern bool Android_OnPadDown(int device_id, int keycode);
 extern bool Android_OnPadUp(int device_id, int keycode);
 extern bool Android_OnJoy(int device_id, int axisnum, float value);
 extern bool Android_OnHat(int device_id, int hat_id, int x, int y);
-extern void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats, bool can_rumble, bool has_rgb_led);
+extern void Android_OnJoySensor(int device_id, int sensor_type, Uint64 sensor_timestamp, float x, float y, float z);
+extern void Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, int button_mask, int naxes, int axis_mask, int nhats,
+   bool can_rumble, bool has_rgb_led, bool has_accelerometer, bool has_gyroscope);
 extern void Android_RemoveJoystick(int device_id);
 
 // A linked list of available joysticks
@@ -47,6 +49,8 @@ typedef struct SDL_joylist_item
     int dpad_state;
     bool can_rumble;
     bool has_rgb_led;
+    bool has_accelerometer;
+    bool has_gyroscope;
 
     struct SDL_joylist_item *next;
 } SDL_joylist_item;