Skip to content

Commit cb04dcd

Browse files
MAJigsaw77slouken
authored andcommitted
android: Add RGB LED support for joysticks.
1 parent 4fcb92e commit cb04dcd

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

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

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
import java.util.List;
77

88
import android.content.Context;
9+
import android.hardware.lights.Light;
10+
import android.hardware.lights.LightsRequest;
11+
import android.hardware.lights.LightsManager;
12+
import android.hardware.lights.LightState;
13+
import android.graphics.Color;
914
import android.os.Build;
1015
import android.os.VibrationEffect;
1116
import android.os.Vibrator;
@@ -25,7 +30,7 @@ public class SDLControllerManager
2530
static native void nativeAddJoystick(int device_id, String name, String desc,
2631
int vendor_id, int product_id,
2732
int button_mask,
28-
int naxes, int axis_mask, int nhats, boolean can_rumble);
33+
int naxes, int axis_mask, int nhats, boolean can_rumble, boolean has_rgb_led);
2934
static native void nativeRemoveJoystick(int device_id);
3035
static native void nativeAddHaptic(int device_id, String name);
3136
static native void nativeRemoveHaptic(int device_id);
@@ -69,6 +74,13 @@ static void pollInputDevices() {
6974
mJoystickHandler.pollInputDevices();
7075
}
7176

77+
/**
78+
* This method is called by SDL using JNI.
79+
*/
80+
static void joystickSetLED(int device_id, int red, int green, int blue) {
81+
mJoystickHandler.setLED(device_id, red, green, blue);
82+
}
83+
7284
/**
7385
* This method is called by SDL using JNI.
7486
*/
@@ -139,6 +151,8 @@ static class SDLJoystick {
139151
String desc;
140152
ArrayList<InputDevice.MotionRange> axes;
141153
ArrayList<InputDevice.MotionRange> hats;
154+
ArrayList<Light> lights;
155+
LightsManager.LightsSession lightsSession;
142156
}
143157
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
144158
@Override
@@ -211,6 +225,7 @@ void pollInputDevices() {
211225
joystick.desc = getJoystickDescriptor(joystickDevice);
212226
joystick.axes = new ArrayList<InputDevice.MotionRange>();
213227
joystick.hats = new ArrayList<InputDevice.MotionRange>();
228+
joystick.lights = new ArrayList<Light>();
214229

215230
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
216231
Collections.sort(ranges, new RangeComparator());
@@ -225,18 +240,30 @@ void pollInputDevices() {
225240
}
226241

227242
boolean can_rumble = false;
243+
boolean has_rgb_led = false;
228244
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
229-
VibratorManager manager = joystickDevice.getVibratorManager();
230-
int[] vibrators = manager.getVibratorIds();
245+
VibratorManager vibratorManager = joystickDevice.getVibratorManager();
246+
int[] vibrators = vibratorManager.getVibratorIds();
231247
if (vibrators.length > 0) {
232248
can_rumble = true;
233249
}
250+
LightsManager lightsManager = joystickDevice.getLightsManager();
251+
List<Light> lights = lightsManager.getLights();
252+
for (Light light : lights) {
253+
if (light.hasRgbControl()) {
254+
joystick.lights.add(light);
255+
}
256+
}
257+
if (!joystick.lights.isEmpty()) {
258+
joystick.lightsSession = lightsManager.openSession();
259+
has_rgb_led = true;
260+
}
234261
}
235262

236263
mJoysticks.add(joystick);
237264
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
238265
getVendorId(joystickDevice), getProductId(joystickDevice),
239-
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble);
266+
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led);
240267
}
241268
}
242269
}
@@ -262,6 +289,16 @@ void pollInputDevices() {
262289
SDLControllerManager.nativeRemoveJoystick(device_id);
263290
for (int i = 0; i < mJoysticks.size(); i++) {
264291
if (mJoysticks.get(i).device_id == device_id) {
292+
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
293+
if (mJoysticks.get(i).lightsSession != null) {
294+
try {
295+
mJoysticks.get(i).lightsSession.close();
296+
} catch (Exception e) {
297+
// Session may already be unregistered when device disconnects
298+
}
299+
mJoysticks.get(i).lightsSession = null;
300+
}
301+
}
265302
mJoysticks.remove(i);
266303
break;
267304
}
@@ -453,6 +490,24 @@ int getButtonMask(InputDevice joystickDevice) {
453490
}
454491
return button_mask;
455492
}
493+
494+
void setLED(int device_id, int red, int green, int blue) {
495+
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
496+
return;
497+
}
498+
SDLJoystick joystick = getJoystick(device_id);
499+
if (joystick == null || joystick.lights.isEmpty()) {
500+
return;
501+
}
502+
LightsRequest.Builder lightsRequest = new LightsRequest.Builder();
503+
LightState lightState = new LightState.Builder().setColor(Color.rgb(red, green, blue)).build();
504+
for (Light light : joystick.lights) {
505+
if (light.hasRgbControl()) {
506+
lightsRequest.addLight(light, lightState);
507+
}
508+
}
509+
joystick.lightsSession.requestLights(lightsRequest.build());
510+
}
456511
}
457512

458513
class SDLHapticHandler_API31 extends SDLHapticHandler {

src/core/android/SDL_android.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
314314
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
315315
JNIEnv *env, jclass jcls,
316316
jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
317-
jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble);
317+
jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led);
318318

319319
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
320320
JNIEnv *env, jclass jcls,
@@ -334,7 +334,7 @@ static JNINativeMethod SDLControllerManager_tab[] = {
334334
{ "onNativePadUp", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) },
335335
{ "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) },
336336
{ "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) },
337-
{ "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
337+
{ "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
338338
{ "nativeRemoveJoystick", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) },
339339
{ "nativeAddHaptic", "(ILjava/lang/String;)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) },
340340
{ "nativeRemoveHaptic", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) }
@@ -406,6 +406,7 @@ static jclass mControllerManagerClass;
406406

407407
// method signatures
408408
static jmethodID midPollInputDevices;
409+
static jmethodID midJoystickSetLED;
409410
static jmethodID midPollHapticDevices;
410411
static jmethodID midHapticRun;
411412
static jmethodID midHapticRumble;
@@ -752,6 +753,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env
752753

753754
midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
754755
"pollInputDevices", "()V");
756+
midJoystickSetLED = (*env)->GetStaticMethodID(env, mControllerManagerClass,
757+
"joystickSetLED", "(IIII)V");
755758
midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
756759
"pollHapticDevices", "()V");
757760
midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
@@ -761,7 +764,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env
761764
midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
762765
"hapticStop", "(I)V");
763766

764-
if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) {
767+
if (!midPollInputDevices || !midJoystickSetLED || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) {
765768
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
766769
}
767770

@@ -1191,13 +1194,13 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
11911194
JNIEnv *env, jclass jcls,
11921195
jint device_id, jstring device_name, jstring device_desc,
11931196
jint vendor_id, jint product_id,
1194-
jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble)
1197+
jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble, jboolean has_rgb_led)
11951198
{
11961199
#ifdef SDL_JOYSTICK_ANDROID
11971200
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
11981201
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
11991202

1200-
Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble);
1203+
Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble, has_rgb_led);
12011204

12021205
(*env)->ReleaseStringUTFChars(env, device_name, name);
12031206
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
@@ -2186,6 +2189,12 @@ void Android_JNI_PollInputDevices(void)
21862189
(*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
21872190
}
21882191

2192+
void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue)
2193+
{
2194+
JNIEnv *env = Android_JNI_GetEnv();
2195+
(*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetLED, device_id, red, green, blue);
2196+
}
2197+
21892198
void Android_JNI_PollHapticDevices(void)
21902199
{
21912200
JNIEnv *env = Android_JNI_GetEnv();

src/core/android/SDL_android.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seco
104104

105105
// Joystick support
106106
void Android_JNI_PollInputDevices(void);
107+
void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue);
107108

108109
// Haptic support
109110
void Android_JNI_PollHapticDevices(void);

src/joystick/android/SDL_sysjoystick.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ bool Android_OnHat(int device_id, int hat_id, int x, int y)
305305
return false;
306306
}
307307

308-
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)
308+
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)
309309
{
310310
SDL_joylist_item *item;
311311
SDL_GUID guid;
@@ -380,6 +380,7 @@ void Android_AddJoystick(int device_id, const char *name, const char *desc, int
380380
item->naxes = naxes;
381381
item->nhats = nhats;
382382
item->can_rumble = can_rumble;
383+
item->has_rgb_led = has_rgb_led;
383384
item->device_instance = SDL_GetNextObjectID();
384385
if (!SDL_joylist_tail) {
385386
SDL_joylist = SDL_joylist_tail = item;
@@ -581,6 +582,10 @@ static bool ANDROID_JoystickOpen(SDL_Joystick *joystick, int device_index)
581582
SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
582583
}
583584

585+
if (item->has_rgb_led) {
586+
SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true);
587+
}
588+
584589
return true;
585590
}
586591

@@ -607,7 +612,15 @@ static bool ANDROID_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_r
607612

608613
static bool ANDROID_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
609614
{
610-
return SDL_Unsupported();
615+
SDL_joylist_item *item = (SDL_joylist_item *)joystick->hwdata;
616+
if (!item) {
617+
return SDL_SetError("SetLED failed, device disconnected");
618+
}
619+
if (!item->has_rgb_led) {
620+
return SDL_Unsupported();
621+
}
622+
Android_JNI_JoystickSetLED(item->device_id, red, green, blue);
623+
return true;
611624
}
612625

613626
static bool ANDROID_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)

src/joystick/android/SDL_sysjoystick_c.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern bool Android_OnPadDown(int device_id, int keycode);
3232
extern bool Android_OnPadUp(int device_id, int keycode);
3333
extern bool Android_OnJoy(int device_id, int axisnum, float value);
3434
extern bool Android_OnHat(int device_id, int hat_id, int x, int y);
35-
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);
35+
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);
3636
extern void Android_RemoveJoystick(int device_id);
3737

3838
// A linked list of available joysticks
@@ -46,6 +46,7 @@ typedef struct SDL_joylist_item
4646
int nbuttons, naxes, nhats;
4747
int dpad_state;
4848
bool can_rumble;
49+
bool has_rgb_led;
4950

5051
struct SDL_joylist_item *next;
5152
} SDL_joylist_item;

0 commit comments

Comments
 (0)