diff --git a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java index 249e3397b..0fffe5d0d 100644 --- a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java +++ b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java @@ -786,6 +786,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD break; } } + + // Light.hasRgbControl() was totally broken prior to Android 14. + // It always returned true because LIGHT_CAPABILITY_RGB was defined as 0, + // so we will just guess RGB is supported if it's a PlayStation controller. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE && context.vendorId != 0x054c) { + context.hasRgbLed = false; + } } // Detect if the gamepad has Mode and Select buttons according to the Android key layouts. @@ -2305,25 +2312,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD // Ignore input devices without an RGB LED if (deviceContext.controllerNumber == controllerNumber && deviceContext.hasRgbLed) { - // Create a new light session if one doesn't already exist - if (deviceContext.lightsSession == null) { - deviceContext.lightsSession = deviceContext.inputDevice.getLightsManager().openSession(); - } - // Convert the RGB components into the integer value that LightState uses - int argbValue = 0xFF000000 | ((r << 16) & 0xFF0000) | ((g << 8) & 0xFF00) | (b & 0xFF); - LightState lightState = new LightState.Builder().setColor(argbValue).build(); + deviceContext.ledArgbValue = 0xFF000000 | ((r << 16) & 0xFF0000) | ((g << 8) & 0xFF00) | (b & 0xFF); - // Set the RGB value for each RGB-controllable LED on the device - LightsRequest.Builder lightsRequestBuilder = new LightsRequest.Builder(); - for (Light light : deviceContext.inputDevice.getLightsManager().getLights()) { - if (light.hasRgbControl()) { - lightsRequestBuilder.addLight(light, lightState); - } - } - - // Apply the LED changes - deviceContext.lightsSession.requestLights(lightsRequestBuilder.build()); + backgroundThreadHandler.removeCallbacks(deviceContext.setLedStateRunnable); + backgroundThreadHandler.post(deviceContext.setLedStateRunnable); } } } @@ -2958,6 +2951,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD public boolean hasRgbLed; public LightsManager.LightsSession lightsSession; + public int ledArgbValue; // These are BatteryState values, not Moonlight values public int lastReportedBatteryStatus; @@ -3034,6 +3028,34 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD } }; + public final Runnable setLedStateRunnable = new Runnable() { + @TargetApi(Build.VERSION_CODES.S) + @Override + public void run() { + LightState lightState = new LightState.Builder().setColor(ledArgbValue).build(); + + // Set the RGB value for each RGB-controllable LED on the device + boolean hasRgbLight = false; + LightsRequest.Builder lightsRequestBuilder = new LightsRequest.Builder(); + for (Light light : inputDevice.getLightsManager().getLights()) { + if (light.hasRgbControl()) { + lightsRequestBuilder.addLight(light, lightState); + hasRgbLight = true; + } + } + + if (hasRgbLight) { + // Create a new light session if one doesn't already exist + if (lightsSession == null) { + lightsSession = inputDevice.getLightsManager().openSession(); + } + + // Apply the LED changes + lightsSession.requestLights(lightsRequestBuilder.build()); + } + } + }; + @Override public void destroy() { super.destroy(); @@ -3046,6 +3068,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD } backgroundThreadHandler.removeCallbacks(enableSensorRunnable); + backgroundThreadHandler.removeCallbacks(setLedStateRunnable); if (gyroListener != null) { sensorManager.unregisterListener(gyroListener); @@ -3056,7 +3079,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (lightsSession != null) { - lightsSession.close(); + try { + lightsSession.close(); + } catch (RuntimeException e) { + LimeLog.warning("Error closing LightsSession: " + e.getMessage()); + } } } @@ -3128,10 +3155,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD capabilities |= MoonBridge.LI_CCAP_BATTERY_STATE; } - // Light.hasRgbControl() was totally broken prior to Android 14. - // It always returned true because LIGHT_CAPABILITY_RGB was defined as 0, - // so we will just guess RGB is supported if it's a PlayStation controller. - if (hasRgbLed && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE || type == MoonBridge.LI_CTYPE_PS)) { + if (hasRgbLed) { capabilities |= MoonBridge.LI_CCAP_RGB_LED; } } @@ -3195,6 +3219,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD this.lightsSession = oldContext.lightsSession; oldContext.lightsSession = null; } + this.ledArgbValue = oldContext.ledArgbValue; this.gyroReportRateHz = oldContext.gyroReportRateHz; this.accelReportRateHz = oldContext.accelReportRateHz; @@ -3219,6 +3244,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD // Re-enable sensors on the new context enableSensors(); + // Refresh the LED state if it's set. We don't know if there was a pending LED state + // callback when the context was migrated, so we'll reset it just to be safe. + if (this.lightsSession != null) { + backgroundThreadHandler.post(setLedStateRunnable); + } + // Refresh battery state and start the battery state polling again backgroundThreadHandler.post(batteryStateUpdateRunnable); }