From f327431bd4158f415af40792ab45e91099f0cab9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 29 Mar 2026 08:27:07 -0700
Subject: [PATCH] Build Maelstrom with SDL 3.4.4
---
.../app/HIDDeviceBLESteamController.java | 128 +++---------------
.../java/org/libsdl/app/HIDDeviceManager.java | 8 +-
.../main/java/org/libsdl/app/SDLActivity.java | 4 +-
external/SDL | 2 +-
game/game.cpp | 13 ++
5 files changed, 33 insertions(+), 122 deletions(-)
diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
index 97b4c127..bf1ca214 100644
--- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
@@ -19,13 +19,9 @@
import java.lang.Runnable;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.UUID;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi";
@@ -41,11 +37,6 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null;
private Handler mHandler;
- private int mProductId = -1;
-
- private static final int D0G_BLE2_PID = 0x1106;
- private static final int TRITON_BLE_PID = 0x1303;
-
private static final int TRANSPORT_AUTO = 0;
private static final int TRANSPORT_BREDR = 1;
@@ -54,13 +45,10 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
- static final UUID inputCharacteristicD0G = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
- static final UUID inputCharacteristicTriton = UUID.fromString("100F6C7A-1735-4313-B402-38567131E5F3");
+ static final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
- private HashMap<Integer, BluetoothGattCharacteristic> mOutputReportChars = new HashMap<Integer, BluetoothGattCharacteristic>();
-
static class GattOperation {
private enum Operation {
CHR_READ,
@@ -326,38 +314,8 @@ private boolean probeService(HIDDeviceBLESteamController controller) {
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
- boolean bShouldStartNotifications = false;
-
- if (chr.getUuid().equals(inputCharacteristicTriton)) {
- Log.v(TAG, "Found Triton input characteristic");
- mProductId = TRITON_BLE_PID;
- bShouldStartNotifications = true;
- } else if (chr.getUuid().equals(inputCharacteristicD0G)) {
- Log.v(TAG, "Found D0G input characteristic");
- mProductId = D0G_BLE2_PID;
- bShouldStartNotifications = true;
- } else {
- Pattern reportPattern = Pattern.compile("100F6C([0-9A-Z]{2})", Pattern.CASE_INSENSITIVE);
- Matcher matcher = reportPattern.matcher(chr.getUuid().toString());
-
- if (matcher.find()) {
- try {
- int reportId = Integer.parseInt(matcher.group(1), 16);
-
- reportId -= 0x35;
- if (reportId >= 0x80) {
- // This is a Triton output report characteristic that we need to care about.
- Log.v(TAG, "Found Triton output report 0x" + Integer.toString(reportId, 16));
- mOutputReportChars.put(reportId, chr);
- }
- }
- catch (NumberFormatException nfe) {
- Log.w(TAG, "Could not parse report characteristic " + chr.getUuid().toString() + ": " + nfe.toString());
- }
- }
- }
-
- if (bShouldStartNotifications) {
+ if (chr.getUuid().equals(inputCharacteristic)) {
+ Log.v(TAG, "Found input characteristic");
// Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) {
@@ -490,16 +448,8 @@ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
mIsConnected = false;
gatt.disconnect();
mGatt = connectGatt(false);
- } else {
- if (getProductId() == TRITON_BLE_PID) {
- // Android will not properly play well with Data Length Extensions without manually requesting a large MTU,
- // and Triton controllers require DLE support.
- //
- // 517 is basically a "magic number" as far as Android's bluetooth code is concerned, so do not change
- // this value. It is functionally "please enable data length extensions" on some Android builds.
- mGatt.requestMtu(517);
- }
-
+ }
+ else {
probeService(this);
}
}
@@ -537,7 +487,7 @@ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteris
// Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
- if (characteristic.getUuid().equals(getInputCharacteristic()) && !mFrozen) {
+ if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
}
}
@@ -552,21 +502,13 @@ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descri
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
- if (chr.getUuid().equals(getInputCharacteristic())) {
+ if (chr.getUuid().equals(inputCharacteristic)) {
boolean hasWrittenInputDescriptor = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
if (reportChr != null) {
- if (getProductId() == TRITON_BLE_PID) {
- // For Triton we just mark things registered.
- Log.v(TAG, "Registering Triton Steam Controller with ID: " + getId());
- mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true);
- setRegistered();
- } else {
- // For the original controller, we need to manually enter Valve mode.
- Log.v(TAG, "Writing report characteristic to enter valve mode");
- reportChr.setValue(enterValveMode);
- gatt.writeCharacteristic(reportChr);
- }
+ Log.v(TAG, "Writing report characteristic to enter valve mode");
+ reportChr.setValue(enterValveMode);
+ gatt.writeCharacteristic(reportChr);
}
}
@@ -606,28 +548,9 @@ public int getVendorId() {
@Override
public int getProductId() {
- if (mProductId > 0) {
- // We've already set a product ID.
- return mProductId;
- }
-
- if (mDevice.getName().startsWith("Steam Ctrl")) {
- // We're a newer Triton device
- mProductId = TRITON_BLE_PID;
- } else {
- // We're an OG Steam Controller
- mProductId = D0G_BLE2_PID;
- }
-
- return mProductId;
- }
-
- private UUID getInputCharacteristic() {
- if (getProductId() == TRITON_BLE_PID) {
- return inputCharacteristicTriton;
- } else {
- return inputCharacteristicD0G;
- }
+ // We don't have an easy way to query from the Bluetooth device, but we know what it is
+ final int D0G_BLE2_PID = 0x1106;
+ return D0G_BLE2_PID;
}
@Override
@@ -678,29 +601,10 @@ public int writeReport(byte[] report, boolean feature) {
writeCharacteristic(reportCharacteristic, actual_report);
return report.length;
} else {
- // If we're an original-recipe Steam Controller we just write to the characteristic directly.
- if (getProductId() == D0G_BLE2_PID) {
- //Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report));
- writeCharacteristic(reportCharacteristic, report);
- return report.length;
- }
-
- // If we're a Triton, we need to find the correct report characteristic.
- if (report.length > 0) {
- int reportId = report[0];
- BluetoothGattCharacteristic targetedReportCharacteristic = mOutputReportChars.get(reportId);
- if (targetedReportCharacteristic != null) {
- byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
- //Log.v(TAG, "writeOutputReport 0x" + Integer.toString(reportId, 16) + " " + HexDump.dumpHexString(report));
- writeCharacteristic(targetedReportCharacteristic.getUuid(), actual_report);
- return report.length;
- } else {
- Log.w(TAG, "Got report write request for unknown report type 0x" + Integer.toString(reportId, 16));
- }
- }
+ //Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report));
+ writeCharacteristic(reportCharacteristic, report);
+ return report.length;
}
-
- return -1;
}
@Override
diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
index b00c905d..1fb2bfb4 100644
--- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
@@ -529,13 +529,7 @@ boolean isSteamController(BluetoothDevice bluetoothDevice) {
return false;
}
- // Steam Controllers will always support Bluetooth Low Energy
- if ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) == 0) {
- return false;
- }
-
- // Match on the name either the original Steam Controller or the new second-generation one advertise with.
- return bluetoothDevice.getName().equals("SteamController") || bluetoothDevice.getName().startsWith("Steam Ctrl");
+ return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
}
private void close() {
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index dcc61d87..0ca7b118 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -60,8 +60,8 @@
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
private static final String TAG = "SDL";
private static final int SDL_MAJOR_VERSION = 3;
- private static final int SDL_MINOR_VERSION = 5;
- private static final int SDL_MICRO_VERSION = 0;
+ private static final int SDL_MINOR_VERSION = 4;
+ private static final int SDL_MICRO_VERSION = 3;
/*
// Display InputType.SOURCE/CLASS of events and devices
//
diff --git a/external/SDL b/external/SDL
index 122ad3d6..1fc5001f 160000
--- a/external/SDL
+++ b/external/SDL
@@ -1 +1 @@
-Subproject commit 122ad3d6f600229dbb4f52883a907699cd3a1acd
+Subproject commit 1fc5001f77d50819c66bbe87b7e266fee247973f
diff --git a/game/game.cpp b/game/game.cpp
index 27e5b478..22ecdafc 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -583,6 +583,19 @@ GamePanelDelegate::OnAction(UIBaseElement *sender, const char *action)
return true;
}
+#if !SDL_VERSION_ATLEAST(3, 5, 0)
+static bool SDL_IsPhone(void)
+{
+#if defined(SDL_PLATFORM_ANDROID) || \
+ (defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_VISIONOS))
+ if (!SDL_IsTablet() && !SDL_IsTV()) {
+ return true;
+ }
+#endif
+ return false;
+}
+#endif // SDL < 3.5.0
+
void
GamePanelDelegate::UpdateZoom()
{