diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java index 4ad0a4b6a015d..9a5a9c3f51187 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -6,6 +6,11 @@ import java.util.List; import android.content.Context; +import android.hardware.lights.Light; +import android.hardware.lights.LightsRequest; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightState; +import android.graphics.Color; import android.os.Build; import android.os.VibrationEffect; import android.os.Vibrator; @@ -25,7 +30,7 @@ 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); + int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led); static native void nativeRemoveJoystick(int device_id); static native void nativeAddHaptic(int device_id, String name); static native void nativeRemoveHaptic(int device_id); @@ -69,6 +74,13 @@ static void pollInputDevices() { mJoystickHandler.pollInputDevices(); } + /** + * This method is called by SDL using JNI. + */ + static void joystickSetLED(int device_id, int red, int green, int blue) { + mJoystickHandler.setLED(device_id, red, green, blue); + } + /** * This method is called by SDL using JNI. */ @@ -139,6 +151,8 @@ static class SDLJoystick { String desc; ArrayList axes; ArrayList hats; + ArrayList lights; + LightsManager.LightsSession lightsSession; } static class RangeComparator implements Comparator { @Override @@ -211,6 +225,7 @@ void pollInputDevices() { joystick.desc = getJoystickDescriptor(joystickDevice); joystick.axes = new ArrayList(); joystick.hats = new ArrayList(); + joystick.lights = new ArrayList(); List ranges = joystickDevice.getMotionRanges(); Collections.sort(ranges, new RangeComparator()); @@ -225,18 +240,30 @@ void pollInputDevices() { } boolean can_rumble = false; + boolean has_rgb_led = false; if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { - VibratorManager manager = joystickDevice.getVibratorManager(); - int[] vibrators = manager.getVibratorIds(); + VibratorManager vibratorManager = joystickDevice.getVibratorManager(); + int[] vibrators = vibratorManager.getVibratorIds(); if (vibrators.length > 0) { can_rumble = true; } + LightsManager lightsManager = joystickDevice.getLightsManager(); + List lights = lightsManager.getLights(); + for (Light light : lights) { + if (light.hasRgbControl()) { + joystick.lights.add(light); + } + } + if (!joystick.lights.isEmpty()) { + joystick.lightsSession = lightsManager.openSession(); + has_rgb_led = 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); + getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led); } } } @@ -262,6 +289,16 @@ void pollInputDevices() { SDLControllerManager.nativeRemoveJoystick(device_id); for (int i = 0; i < mJoysticks.size(); i++) { if (mJoysticks.get(i).device_id == device_id) { + if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { + if (mJoysticks.get(i).lightsSession != null) { + try { + mJoysticks.get(i).lightsSession.close(); + } catch (Exception e) { + // Session may already be unregistered when device disconnects + } + mJoysticks.get(i).lightsSession = null; + } + } mJoysticks.remove(i); break; } @@ -453,6 +490,24 @@ int getButtonMask(InputDevice joystickDevice) { } return button_mask; } + + void setLED(int device_id, int red, int green, int blue) { + if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) { + return; + } + SDLJoystick joystick = getJoystick(device_id); + if (joystick == null || joystick.lights.isEmpty()) { + return; + } + LightsRequest.Builder lightsRequest = new LightsRequest.Builder(); + LightState lightState = new LightState.Builder().setColor(Color.rgb(red, green, blue)).build(); + for (Light light : joystick.lights) { + if (light.hasRgbControl()) { + lightsRequest.addLight(light, lightState); + } + } + joystick.lightsSession.requestLights(lightsRequest.build()); + } } class SDLHapticHandler_API31 extends SDLHapticHandler { diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index 7623d8bae01ab..8fd18a77cda21 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -314,7 +314,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( 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); + jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led); JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)( JNIEnv *env, jclass jcls, @@ -334,7 +334,7 @@ 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;IIIIIIZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) }, + { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZ)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) } @@ -406,6 +406,7 @@ static jclass mControllerManagerClass; // method signatures static jmethodID midPollInputDevices; +static jmethodID midJoystickSetLED; static jmethodID midPollHapticDevices; static jmethodID midHapticRun; static jmethodID midHapticRumble; @@ -752,6 +753,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, "pollInputDevices", "()V"); + midJoystickSetLED = (*env)->GetStaticMethodID(env, mControllerManagerClass, + "joystickSetLED", "(IIII)V"); midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, "pollHapticDevices", "()V"); midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass, @@ -761,7 +764,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass, "hapticStop", "(I)V"); - if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { + if (!midPollInputDevices || !midJoystickSetLED || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?"); } @@ -1180,13 +1183,13 @@ 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) + jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led) { #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); + Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble, has_rgb_led); (*env)->ReleaseStringUTFChars(env, device_name, name); (*env)->ReleaseStringUTFChars(env, device_desc, desc); @@ -2175,6 +2178,12 @@ void Android_JNI_PollInputDevices(void) (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices); } +void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue) +{ + JNIEnv *env = Android_JNI_GetEnv(); + (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetLED, device_id, red, green, blue); +} + void Android_JNI_PollHapticDevices(void) { JNIEnv *env = Android_JNI_GetEnv(); diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index 251a7bbd2e7c5..f6df8717fc042 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -104,6 +104,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); // Haptic support void Android_JNI_PollHapticDevices(void); diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index e3693686eb658..e6b0fa5a7a28f 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -305,7 +305,7 @@ 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) +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) { SDL_joylist_item *item; SDL_GUID guid; @@ -380,6 +380,7 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int item->naxes = naxes; item->nhats = nhats; item->can_rumble = can_rumble; + item->has_rgb_led = has_rgb_led; item->device_instance = SDL_GetNextObjectID(); if (!SDL_joylist_tail) { SDL_joylist = SDL_joylist_tail = item; @@ -581,6 +582,10 @@ static bool ANDROID_JoystickOpen(SDL_Joystick *joystick, int device_index) SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); } + if (item->has_rgb_led) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true); + } + return true; } @@ -607,7 +612,15 @@ static bool ANDROID_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_r static bool ANDROID_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) { - return SDL_Unsupported(); + SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata; + if (!item) { + return SDL_SetError("SetLED failed, device disconnected"); + } + if (!item->has_rgb_led) { + return SDL_Unsupported(); + } + Android_JNI_JoystickSetLED(item->device_id, red, green, blue); + return true; } static bool ANDROID_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) diff --git a/src/joystick/android/SDL_sysjoystick_c.h b/src/joystick/android/SDL_sysjoystick_c.h index febb2288832bf..109ea3b630c95 100644 --- a/src/joystick/android/SDL_sysjoystick_c.h +++ b/src/joystick/android/SDL_sysjoystick_c.h @@ -32,7 +32,7 @@ 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); +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_RemoveJoystick(int device_id); // A linked list of available joysticks @@ -46,6 +46,7 @@ typedef struct SDL_joylist_item int nbuttons, naxes, nhats; int dpad_state; bool can_rumble; + bool has_rgb_led; struct SDL_joylist_item *next; } SDL_joylist_item;