Updated SDL3 to 3.4.16, and disabled Wayland for now

This commit is contained in:
Yousef Khadadeh 2026-09-07 18:00:01 +03:00
parent 56aada6cc6
commit 5729057e32
14 changed files with 457 additions and 104 deletions

View file

@ -402,7 +402,7 @@ elseif(LINUX)
set(SDL_TESTS OFF CACHE BOOL "Compile SDL tests") set(SDL_TESTS OFF CACHE BOOL "Compile SDL tests")
set(SDL_INSTALL_DOCS OFF CACHE BOOL "Install docs") set(SDL_INSTALL_DOCS OFF CACHE BOOL "Install docs")
set(SDL_INSTALL_TESTS OFF CACHE BOOL "Install tests") set(SDL_INSTALL_TESTS OFF CACHE BOOL "Install tests")
set(SDL_WAYLAND ON CACHE BOOL "Compile SDL for Wayland") set(SDL_WAYLAND OFF CACHE BOOL "Compile SDL for Wayland")
set(SDL_X11 ON CACHE BOOL "Compile SDL for X11") set(SDL_X11 ON CACHE BOOL "Compile SDL for X11")
set(SDL_AUDIO OFF CACHE BOOL "Compile SDL Audio") set(SDL_AUDIO OFF CACHE BOOL "Compile SDL Audio")
set(SDL_STATIC ON CACHE BOOL "Compile SDL statically") set(SDL_STATIC ON CACHE BOOL "Compile SDL statically")

View file

@ -56,10 +56,11 @@
boolean setSystemCursor(int); boolean setSystemCursor(int);
void setWindowStyle(boolean); void setWindowStyle(boolean);
boolean shouldMinimizeOnFocusLoss(); boolean shouldMinimizeOnFocusLoss();
boolean showFileDialog(java.lang.String[], boolean, boolean, int);
boolean showTextInput(int, int, int, int, int); boolean showTextInput(int, int, int, int, int);
boolean supportsRelativeMouse(); boolean supportsRelativeMouse();
int openFileDescriptor(java.lang.String, java.lang.String); int openFileDescriptor(java.lang.String, java.lang.String);
boolean showFileDialog(java.lang.String[], boolean, boolean, int); boolean showFileDialog(java.lang.String[], boolean, int, java.lang.String, int);
java.lang.String getPreferredLocales(); java.lang.String getPreferredLocales();
java.lang.String formatLocale(java.util.Locale); java.lang.String formatLocale(java.util.Locale);
} }
@ -79,6 +80,7 @@
} }
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager { -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager {
void joystickSetSensorsEnabled(int, boolean);
void pollInputDevices(); void pollInputDevices();
void joystickSetLED(int, int, int, int); void joystickSetLED(int, int, int, int);
void pollHapticDevices(); void pollHapticDevices();

View file

@ -19,9 +19,13 @@ import android.os.*;
import java.lang.Runnable; import java.lang.Runnable;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice { class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
private static final String TAG = "hidapi"; private static final String TAG = "hidapi";
@ -33,10 +37,19 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
private boolean mIsConnected = false; private boolean mIsConnected = false;
private boolean mIsChromebook = false; private boolean mIsChromebook = false;
private boolean mIsReconnecting = false; private boolean mIsReconnecting = false;
private boolean mHasEnabledNotifications = false;
private boolean mHasSeenInputUpdate = false;
private boolean mFrozen = false; private boolean mFrozen = false;
private LinkedList<GattOperation> mOperations; private LinkedList<GattOperation> mOperations;
GattOperation mCurrentOperation = null; GattOperation mCurrentOperation = null;
private Handler mHandler; private Handler mHandler;
private int mProductId = -1;
private int mReportId = 0;
private UUID mInputCharacteristic;
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_AUTO = 0;
private static final int TRANSPORT_BREDR = 1; private static final int TRANSPORT_BREDR = 1;
@ -45,10 +58,14 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000; private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3"); static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
static final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3"); static final UUID inputCharacteristicD0G = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
static final UUID inputCharacteristicTriton_0x45 = UUID.fromString("100F6C7A-1735-4313-B402-38567131E5F3");
static final UUID inputCharacteristicTriton_0x47 = UUID.fromString("100F6C7C-1735-4313-B402-38567131E5F3");
static final UUID reportCharacteristic = UUID.fromString("100F6C34-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 }; 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 { static class GattOperation {
private enum Operation { private enum Operation {
CHR_READ, CHR_READ,
@ -61,6 +78,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
byte[] mValue; byte[] mValue;
BluetoothGatt mGatt; BluetoothGatt mGatt;
boolean mResult = true; boolean mResult = true;
int mDelayMs = 0;
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) { private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
mGatt = gatt; mGatt = gatt;
@ -68,6 +86,13 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mUuid = uuid; mUuid = uuid;
} }
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, int delayMs) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mDelayMs = delayMs;
}
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) { private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
mGatt = gatt; mGatt = gatt;
mOp = operation; mOp = operation;
@ -75,6 +100,14 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mValue = value; mValue = value;
} }
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value, int delayMs) {
mGatt = gatt;
mOp = operation;
mUuid = uuid;
mValue = value;
mDelayMs = delayMs;
}
public void run() { public void run() {
// This is executed in main thread // This is executed in main thread
BluetoothGattCharacteristic chr; BluetoothGattCharacteristic chr;
@ -136,6 +169,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
return mResult; return mResult;
} }
public int getDelayMs() { return mDelayMs; }
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattService valveService = mGatt.getService(steamControllerService); BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null) if (valveService == null)
@ -154,6 +189,10 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) { static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid); return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
} }
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid, int delayMs) {
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid, delayMs);
}
} }
HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) { HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
@ -166,6 +205,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mHandler = new Handler(Looper.getMainLooper()); mHandler = new Handler(Looper.getMainLooper());
mGatt = connectGatt(); mGatt = connectGatt();
mHasEnabledNotifications = false;
mHasSeenInputUpdate = false;
// final HIDDeviceBLESteamController finalThis = this; // final HIDDeviceBLESteamController finalThis = this;
// mHandler.postDelayed(new Runnable() { // mHandler.postDelayed(new Runnable() {
// @Override // @Override
@ -314,8 +355,45 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
Log.v(TAG, "Found Valve steam controller service " + service.getUuid()); Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) { for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(inputCharacteristic)) { if (chr.getUuid().equals(inputCharacteristicTriton_0x45)) {
Log.v(TAG, "Found input characteristic"); Log.v(TAG, "Found Triton input characteristic 0x45");
mProductId = TRITON_BLE_PID;
mReportId = 0x45;
mInputCharacteristic = chr.getUuid();
} else if (chr.getUuid().equals(inputCharacteristicTriton_0x47)) {
Log.v(TAG, "Found Triton input characteristic 0x47");
mProductId = TRITON_BLE_PID;
mReportId = 0x47;
mInputCharacteristic = chr.getUuid();
} else if (chr.getUuid().equals(inputCharacteristicD0G)) {
Log.v(TAG, "Found D0G input characteristic");
mProductId = D0G_BLE2_PID;
mReportId = 0x03;
mInputCharacteristic = chr.getUuid();
} 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());
}
}
}
}
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
if (chr.getUuid().equals(mInputCharacteristic)) {
// Start notifications // Start notifications
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (cccd != null) { if (cccd != null) {
@ -372,21 +450,30 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mCurrentOperation = mOperations.removeFirst(); mCurrentOperation = mOperations.removeFirst();
} }
// Run in main thread Runnable gattOperationRunnable = new Runnable() {
mHandler.post(new Runnable() { @Override
@Override public void run() {
public void run() { synchronized (mOperations) {
synchronized (mOperations) { if (mCurrentOperation == null) {
if (mCurrentOperation == null) { Log.e(TAG, "Current operation null in executor?");
Log.e(TAG, "Current operation null in executor?"); return;
return; }
}
mCurrentOperation.run(); mCurrentOperation.run();
// now wait for the GATT callback and when it comes, finish this operation // now wait for the GATT callback and when it comes, finish this operation
}
} }
} };
});
if (mCurrentOperation.getDelayMs() == 0) {
// Run in main thread
mHandler.post(gattOperationRunnable);
}
else {
// If we have a delay on this operation, wait before we post it.
mHandler.postDelayed(gattOperationRunnable, mCurrentOperation.getDelayMs());
}
} }
private void queueGattOperation(GattOperation op) { private void queueGattOperation(GattOperation op) {
@ -397,8 +484,39 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
} }
private void enableNotification(UUID chrUuid) { private void enableNotification(UUID chrUuid) {
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid); // Add a 500ms delay to notification write for Amazon Fire TV devices, as otherwise if we do this too quickly after connecting
// it will return success and then silently drop the operation on the floor.
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid, 500);
queueGattOperation(op); queueGattOperation(op);
// Amazon Fire devices can also silently timeout on writeDescriptor, so
// set up a little delayed check that will attempt to write a second time.
//
// While this only seems to be needed on Amazon Fire TV devices at present, it
// doesn't hurt to have a retry on other devices as well.
//
final HIDDeviceBLESteamController finalThis = this;
final UUID finalUuid = chrUuid;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (!finalThis.mHasEnabledNotifications) {
if (finalThis.mHasSeenInputUpdate) {
// Amazon Five devices may have enabled notifications on the input characteristic and not given us a callback. If we've seen
// input reports, though, somewhat by definition notifications are enabled.
Log.w(TAG, "WriteDescriptor has never returned, but we've seen input reports. Moving on with controller initialization.");
finalThis.mHasEnabledNotifications = true;
finalThis.enableValveMode();
return;
}
// Give one more try.
GattOperation retry = HIDDeviceBLESteamController.GattOperation.enableNotification(finalThis.mGatt, finalUuid, 500);
finalThis.queueGattOperation(retry);
}
}
}, 1000);
} }
void writeCharacteristic(UUID uuid, byte[] value) { void writeCharacteristic(UUID uuid, byte[] value) {
@ -448,8 +566,16 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
mIsConnected = false; mIsConnected = false;
gatt.disconnect(); gatt.disconnect();
mGatt = connectGatt(false); mGatt = connectGatt(false);
} } else {
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);
}
probeService(this); probeService(this);
} }
} }
@ -474,7 +600,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
// Only register controller with the native side once it has been fully configured // Only register controller with the native side once it has been fully configured
if (!isRegistered()) { if (!isRegistered()) {
Log.v(TAG, "Registering Steam Controller with ID: " + getId()); Log.v(TAG, "Registering Steam Controller with ID: " + getId());
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true); mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true, mReportId);
setRegistered(); setRegistered();
} }
} }
@ -487,7 +613,8 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
// Enable this for verbose logging of controller input reports // Enable this for verbose logging of controller input reports
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue())); //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) { if (characteristic.getUuid().equals(mInputCharacteristic) && !mFrozen) {
mHasSeenInputUpdate = true;
mManager.HIDDeviceInputReport(getId(), characteristic.getValue()); mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
} }
} }
@ -497,19 +624,36 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
//Log.v(TAG, "onDescriptorRead status=" + status); //Log.v(TAG, "onDescriptorRead status=" + status);
} }
private void enableValveMode()
{
BluetoothGattService valveService = mGatt.getService(steamControllerService);
if (valveService == null)
return;
BluetoothGattCharacteristic reportChr = valveService.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, mReportId);
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);
mGatt.writeCharacteristic(reportChr);
}
}
}
@Override @Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
BluetoothGattCharacteristic chr = descriptor.getCharacteristic(); BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid()); //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
if (chr.getUuid().equals(inputCharacteristic)) { if (chr.getUuid().equals(mInputCharacteristic)) {
boolean hasWrittenInputDescriptor = true; mHasEnabledNotifications = true;
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic); enableValveMode();
if (reportChr != null) {
Log.v(TAG, "Writing report characteristic to enter valve mode");
reportChr.setValue(enterValveMode);
gatt.writeCharacteristic(reportChr);
}
} }
finishCurrentGattOperation(); finishCurrentGattOperation();
@ -548,9 +692,20 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
@Override @Override
public int getProductId() { public int getProductId() {
// We don't have an easy way to query from the Bluetooth device, but we know what it is if (mProductId > 0) {
final int D0G_BLE2_PID = 0x1106; // We've already set a product ID.
return D0G_BLE2_PID; 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;
} }
@Override @Override
@ -601,10 +756,29 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
writeCharacteristic(reportCharacteristic, actual_report); writeCharacteristic(reportCharacteristic, actual_report);
return report.length; return report.length;
} else { } else {
//Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report)); // If we're an original-recipe Steam Controller we just write to the characteristic directly.
writeCharacteristic(reportCharacteristic, report); if (getProductId() == D0G_BLE2_PID) {
return report.length; //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] & 0xFF;
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));
}
}
} }
return -1;
} }
@Override @Override

View file

@ -232,6 +232,7 @@ public class HIDDeviceManager {
final int XB360W_IFACE_PROTOCOL = 129; // Wireless final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = { final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2 0x0079, // GPD Win 2
0x0351, // CRKD
0x044f, // Thrustmaster 0x044f, // Thrustmaster
0x045e, // Microsoft 0x045e, // Microsoft
0x046d, // Logitech 0x046d, // Logitech
@ -242,7 +243,9 @@ public class HIDDeviceManager {
0x0e6f, // PDP 0x0e6f, // PDP
0x0f0d, // Hori 0x0f0d, // Hori
0x1038, // SteelSeries 0x1038, // SteelSeries
0x10f5, // Turtle Beach
0x11c9, // Nacon 0x11c9, // Nacon
0x1209, // Generic
0x12ab, // Unknown 0x12ab, // Unknown
0x1430, // RedOctane 0x1430, // RedOctane
0x146b, // BigBen 0x146b, // BigBen
@ -256,7 +259,10 @@ public class HIDDeviceManager {
0x24c6, // PowerA 0x24c6, // PowerA
0x2c22, // Qanba 0x2c22, // Qanba
0x2dc8, // 8BitDo 0x2dc8, // 8BitDo
0x3537, // GameSir
0x3651, // CRKD
0x37d7, // Flydigi 0x37d7, // Flydigi
0x3958, // Red Octane Games
0x9886, // ASTRO Gaming 0x9886, // ASTRO Gaming
}; };
@ -278,6 +284,7 @@ public class HIDDeviceManager {
final int XB1_IFACE_SUBCLASS = 71; final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208; final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = { final int[] SUPPORTED_VENDORS = {
0x0351, // CRKD
0x03f0, // HP 0x03f0, // HP
0x044f, // Thrustmaster 0x044f, // Thrustmaster
0x045e, // Microsoft 0x045e, // Microsoft
@ -286,6 +293,7 @@ public class HIDDeviceManager {
0x0e6f, // PDP 0x0e6f, // PDP
0x0f0d, // Hori 0x0f0d, // Hori
0x10f5, // Turtle Beach 0x10f5, // Turtle Beach
0x1209, // Generic
0x1532, // Razer Wildcat 0x1532, // Razer Wildcat
0x20d6, // PowerA 0x20d6, // PowerA
0x24c6, // PowerA 0x24c6, // PowerA
@ -295,7 +303,9 @@ public class HIDDeviceManager {
0x2e95, // SCUF 0x2e95, // SCUF
0x3285, // Nacon 0x3285, // Nacon
0x3537, // GameSir 0x3537, // GameSir
0x3651, // CRKD
0x366c, // ByoWave 0x366c, // ByoWave
0x3958, // Red Octane Games
}; };
if (usbInterface.getId() == 0 && if (usbInterface.getId() == 0 &&
@ -360,7 +370,7 @@ public class HIDDeviceManager {
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index); HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
int id = device.getId(); int id = device.getId();
mDevicesById.put(id, device); mDevicesById.put(id, device);
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol(), false); HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol(), false, 0);
} }
} }
} }
@ -529,7 +539,13 @@ public class HIDDeviceManager {
return false; return false;
} }
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0); // 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");
} }
private void close() { private void close() {
@ -681,7 +697,7 @@ public class HIDDeviceManager {
private native void HIDDeviceRegisterCallback(); private native void HIDDeviceRegisterCallback();
private native void HIDDeviceReleaseCallback(); private native void HIDDeviceReleaseCallback();
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol, boolean bBluetooth); native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol, boolean bBluetooth, int reportID);
native void HIDDeviceOpenPending(int deviceID); native void HIDDeviceOpenPending(int deviceID);
native void HIDDeviceOpenResult(int deviceID, boolean opened); native void HIDDeviceOpenResult(int deviceID, boolean opened);
native void HIDDeviceDisconnected(int deviceID); native void HIDDeviceDisconnected(int deviceID);

View file

@ -21,6 +21,7 @@ class HIDDeviceUSB implements HIDDevice {
protected InputThread mInputThread; protected InputThread mInputThread;
protected boolean mRunning; protected boolean mRunning;
protected boolean mFrozen; protected boolean mFrozen;
protected boolean mClaimed;
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) { public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
mManager = manager; mManager = manager;
@ -29,6 +30,7 @@ class HIDDeviceUSB implements HIDDevice {
mInterface = mDevice.getInterface(mInterfaceIndex).getId(); mInterface = mDevice.getInterface(mInterfaceIndex).getId();
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
mRunning = false; mRunning = false;
mClaimed = false;
} }
String getIdentifier() { String getIdentifier() {
@ -56,7 +58,7 @@ class HIDDeviceUSB implements HIDDevice {
try { try {
result = mDevice.getSerialNumber(); result = mDevice.getSerialNumber();
} }
catch (SecurityException exception) { catch (Exception exception) {
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage()); //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
} }
if (result == null) { if (result == null) {
@ -114,6 +116,7 @@ class HIDDeviceUSB implements HIDDevice {
close(); close();
return false; return false;
} }
mClaimed = true;
// Find the endpoints // Find the endpoints
for (int j = 0; j < iface.getEndpointCount(); j++) { for (int j = 0; j < iface.getEndpointCount(); j++) {
@ -132,9 +135,12 @@ class HIDDeviceUSB implements HIDDevice {
} }
} }
// Make sure the required endpoints were present // Make sure the required endpoints were present. The original Steam Controller and the wireless dongle for it do NOT
if (mInputEndpoint == null || mOutputEndpoint == null) { // actually have -- or require -- output endpoints, so we need to accept only an input one for them or else we'll fall
// back to the Android system gamepad functionality (and lose our paddles et al).
if (mInputEndpoint == null) {
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName()); Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
mConnection.releaseInterface(iface);
close(); close();
return false; return false;
} }
@ -154,6 +160,11 @@ class HIDDeviceUSB implements HIDDevice {
return -1; return -1;
} }
if (!mClaimed) {
Log.w(TAG, "writeReport() called but some other process currently owns the USB device");
return -1;
}
if (feature) { if (feature) {
int res = -1; int res = -1;
int offset = 0; int offset = 0;
@ -185,6 +196,11 @@ class HIDDeviceUSB implements HIDDevice {
} }
return length; return length;
} else { } else {
if (mOutputEndpoint == null)
{
Log.e(TAG, "Tried to write an output report to an interface with no output endpoint!");
return -1;
}
int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000); int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
if (res != report.length) { if (res != report.length) {
Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName()); Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName());
@ -205,6 +221,12 @@ class HIDDeviceUSB implements HIDDevice {
Log.w(TAG, "readReport() called with no device connection"); Log.w(TAG, "readReport() called with no device connection");
return false; return false;
} }
if (!mClaimed) {
if (feature) {
return false;
}
return true;
}
if (report_number == 0x0) { if (report_number == 0x0) {
/* Offset the return buffer by 1, so that the report ID /* Offset the return buffer by 1, so that the report ID
@ -258,10 +280,13 @@ class HIDDeviceUSB implements HIDDevice {
mInputThread = null; mInputThread = null;
} }
if (mConnection != null) { if (mConnection != null) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex); if (mClaimed) {
mConnection.releaseInterface(iface); UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
mConnection.releaseInterface(iface);
}
mConnection.close(); mConnection.close();
mConnection = null; mConnection = null;
mClaimed = false;
} }
} }
@ -274,6 +299,22 @@ class HIDDeviceUSB implements HIDDevice {
@Override @Override
public void setFrozen(boolean frozen) { public void setFrozen(boolean frozen) {
mFrozen = frozen; mFrozen = frozen;
/* If we have a valid device connection and the claim state doesn't match what we want, try to correct that. */
if (mConnection != null && mClaimed == mFrozen) {
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
if (frozen) {
mClaimed = !mConnection.releaseInterface(iface);
if (mClaimed) {
Log.e(TAG, "Tried to release claim on USB device, but failed!");
}
} else {
mClaimed = mConnection.claimInterface(iface, true);
if (!mClaimed) {
Log.e(TAG, "Tried to regain claim on USB device, but failed!");
}
}
}
} }
protected class InputThread extends Thread { protected class InputThread extends Thread {

View file

@ -63,7 +63,7 @@ public abstract class SDLActivity extends Activity implements View.OnSystemUiVis
private static final String TAG = "SDL"; private static final String TAG = "SDL";
private static final int SDL_MAJOR_VERSION = 3; private static final int SDL_MAJOR_VERSION = 3;
private static final int SDL_MINOR_VERSION = 4; private static final int SDL_MINOR_VERSION = 4;
private static final int SDL_MICRO_VERSION = 8; private static final int SDL_MICRO_VERSION = 16;
/* /*
// Display InputType.SOURCE/CLASS of events and devices // Display InputType.SOURCE/CLASS of events and devices
// //
@ -535,6 +535,7 @@ public abstract class SDLActivity extends Activity implements View.OnSystemUiVis
if (mHIDDeviceManager != null) { if (mHIDDeviceManager != null) {
mHIDDeviceManager.setFrozen(true); mHIDDeviceManager.setFrozen(true);
} }
if (!mHasMultiWindow) { if (!mHasMultiWindow) {
pauseNativeThread(); pauseNativeThread();
} }
@ -548,6 +549,7 @@ public abstract class SDLActivity extends Activity implements View.OnSystemUiVis
if (mHIDDeviceManager != null) { if (mHIDDeviceManager != null) {
mHIDDeviceManager.setFrozen(false); mHIDDeviceManager.setFrozen(false);
} }
if (!mHasMultiWindow) { if (!mHasMultiWindow) {
resumeNativeThread(); resumeNativeThread();
} }
@ -620,6 +622,14 @@ public abstract class SDLActivity extends Activity implements View.OnSystemUiVis
super.onWindowFocusChanged(hasFocus); super.onWindowFocusChanged(hasFocus);
Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
// If we are gaining focus, we can always try to restore our USB devices. If we are losing focus,
// only try to relinquish them if we don't have background events allowed (for multi-window Android setups).
if (hasFocus || !SDLActivity.nativeGetHintBoolean("SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", false)) {
if (mHIDDeviceManager != null) {
mHIDDeviceManager.setFrozen(!hasFocus);
}
}
if (SDLActivity.mBrokenLibraries) { if (SDLActivity.mBrokenLibraries) {
return; return;
} }
@ -1485,11 +1495,11 @@ public abstract class SDLActivity extends Activity implements View.OnSystemUiVis
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) { if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
// Note that we process events with specific key codes here // Note that we process events with specific key codes here
if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLControllerManager.onNativePadDown(deviceId, keyCode)) { if (SDLControllerManager.onNativePadDown(deviceId, keyCode, event.getScanCode())) {
return true; return true;
} }
} else if (event.getAction() == KeyEvent.ACTION_UP) { } else if (event.getAction() == KeyEvent.ACTION_UP) {
if (SDLControllerManager.onNativePadUp(deviceId, keyCode)) { if (SDLControllerManager.onNativePadUp(deviceId, keyCode, event.getScanCode())) {
return true; return true;
} }
} }

View file

@ -10,6 +10,10 @@ import android.hardware.lights.Light;
import android.hardware.lights.LightsRequest; import android.hardware.lights.LightsRequest;
import android.hardware.lights.LightsManager; import android.hardware.lights.LightsManager;
import android.hardware.lights.LightState; 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.graphics.Color;
import android.os.Build; import android.os.Build;
import android.os.VibrationEffect; import android.os.VibrationEffect;
@ -30,16 +34,18 @@ public class SDLControllerManager
static native void nativeAddJoystick(int device_id, String name, String desc, static native void nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id, int vendor_id, int product_id,
int button_mask, 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 nativeRemoveJoystick(int device_id);
static native void nativeAddHaptic(int device_id, String name); static native void nativeAddHaptic(int device_id, String name);
static native void nativeRemoveHaptic(int device_id); static native void nativeRemoveHaptic(int device_id);
static public native boolean onNativePadDown(int device_id, int keycode); static public native boolean onNativePadDown(int device_id, int keycode, int scancode);
static public native boolean onNativePadUp(int device_id, int keycode); static public native boolean onNativePadUp(int device_id, int keycode, int scancode);
static native void onNativeJoy(int device_id, int axis, static native void onNativeJoy(int device_id, int axis,
float value); float value);
static native void onNativeHat(int device_id, int hat_id, static native void onNativeHat(int device_id, int hat_id,
int x, int y); 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 SDLJoystickHandler mJoystickHandler;
protected static SDLHapticHandler mHapticHandler; protected static SDLHapticHandler mHapticHandler;
@ -81,6 +87,13 @@ public class SDLControllerManager
mJoystickHandler.setLED(device_id, red, green, blue); 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. * This method is called by SDL using JNI.
*/ */
@ -153,6 +166,10 @@ class SDLJoystickHandler {
ArrayList<InputDevice.MotionRange> hats; ArrayList<InputDevice.MotionRange> hats;
ArrayList<Light> lights; ArrayList<Light> lights;
LightsManager.LightsSession lightsSession; LightsManager.LightsSession lightsSession;
SensorManager sensorManager;
SDLJoySensorListener sensorListener;
Sensor accelerometerSensor;
Sensor gyroscopeSensor;
} }
static class RangeComparator implements Comparator<InputDevice.MotionRange> { static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override @Override
@ -225,12 +242,13 @@ class SDLJoystickHandler {
joystick.desc = getJoystickDescriptor(joystickDevice); joystick.desc = getJoystickDescriptor(joystickDevice);
joystick.axes = new ArrayList<InputDevice.MotionRange>(); joystick.axes = new ArrayList<InputDevice.MotionRange>();
joystick.hats = new ArrayList<InputDevice.MotionRange>(); joystick.hats = new ArrayList<InputDevice.MotionRange>();
java.util.Set<Integer> axisStrsSet = new java.util.HashSet<Integer>();
joystick.lights = new ArrayList<Light>(); joystick.lights = new ArrayList<Light>();
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges(); List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator()); Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges) { for (InputDevice.MotionRange range : ranges) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { if (((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) && axisStrsSet.add(range.getAxis())) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range); joystick.hats.add(range);
} else { } else {
@ -241,6 +259,8 @@ class SDLJoystickHandler {
boolean can_rumble = false; boolean can_rumble = false;
boolean has_rgb_led = false; boolean has_rgb_led = false;
boolean has_accelerometer = false;
boolean has_gyroscope = false;
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
VibratorManager vibratorManager = joystickDevice.getVibratorManager(); VibratorManager vibratorManager = joystickDevice.getVibratorManager();
int[] vibrators = vibratorManager.getVibratorIds(); int[] vibrators = vibratorManager.getVibratorIds();
@ -258,12 +278,26 @@ class SDLJoystickHandler {
joystick.lightsSession = lightsManager.openSession(); joystick.lightsSession = lightsManager.openSession();
has_rgb_led = true; 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); mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
getVendorId(joystickDevice), getProductId(joystickDevice), 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 +542,31 @@ class SDLJoystickHandler {
} }
joystick.lightsSession.requestLights(lightsRequest.build()); 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) {
SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
}
if (joystick.gyroscopeSensor != null) {
SDLSensorManager.registerListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor, SensorManager.SENSOR_DELAY_GAME);
}
} else {
if (joystick.accelerometerSensor != null) {
SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.accelerometerSensor);
}
if (joystick.gyroscopeSensor != null) {
SDLSensorManager.unregisterListener(joystick.sensorManager, joystick.sensorListener, joystick.gyroscopeSensor);
}
}
}
} }
class SDLHapticHandler_API31 extends SDLHapticHandler { class SDLHapticHandler_API31 extends SDLHapticHandler {
@ -933,3 +992,19 @@ class SDLGenericMotionListener_API29 extends SDLGenericMotionListener_API26 {
return penDevice.isExternal() ? SDL_PEN_DEVICE_TYPE_INDIRECT : SDL_PEN_DEVICE_TYPE_DIRECT; 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]);
}
}

View file

@ -0,0 +1,73 @@
package org.libsdl.app;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
// This class coordinates synchronized access to sensor manager registration
//
// This prevents a java.util.ConcurrentModificationException exception on
// Android 16, specifically on the Samsung Tab S9 Ultra.
class SDLSensorManager
{
static private SDLSensorManager mManager = new SDLSensorManager();
static final int RETRY_COUNT = 3;
public static void registerListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) {
mManager.RegisterListener(manager, listener, sensor, samplingPeriodUs);
}
public static void unregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) {
mManager.UnregisterListener(manager, listener, sensor);
}
private synchronized void RegisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor, int samplingPeriodUs) {
int retries = 0;
boolean complete = false;
while (!complete) {
try {
manager.registerListener(listener, sensor, samplingPeriodUs, null);
complete = true;
} catch (java.util.ConcurrentModificationException e) {
++retries;
if (retries <= RETRY_COUNT) {
// Sleep a bit and try again
try {
Thread.sleep(1);
} catch (Exception e2) {
}
} else {
Log.v("SDL", "Multiple ConcurrentModificationException caught while registering sensor listener, canceling operation");
complete = true;
}
}
}
}
private synchronized void UnregisterListener(SensorManager manager, SensorEventListener listener, Sensor sensor) {
int retries = 0;
boolean complete = false;
while (!complete) {
try {
manager.unregisterListener(listener, sensor);
complete = true;
} catch (java.util.ConcurrentModificationException e) {
++retries;
if (retries <= RETRY_COUNT) {
// Sleep a bit and try again
try {
Thread.sleep(1);
} catch (Exception e2) {
}
} else {
Log.v("SDL", "Multiple ConcurrentModificationException caught while unregistering sensor listener, canceling operation");
complete = true;
}
}
}
}
}

View file

@ -328,11 +328,11 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
protected void enableSensor(int sensortype, boolean enabled) { protected void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels? // TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) { if (enabled) {
mSensorManager.registerListener(this, SDLSensorManager.registerListener(mSensorManager, this,
mSensorManager.getDefaultSensor(sensortype), mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null); SensorManager.SENSOR_DELAY_GAME);
} else { } else {
mSensorManager.unregisterListener(this, SDLSensorManager.unregisterListener(mSensorManager, this,
mSensorManager.getDefaultSensor(sensortype)); mSensorManager.getDefaultSensor(sensortype));
} }
} }
@ -402,45 +402,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
@Override @Override
public boolean onCapturedPointerEvent(MotionEvent event) public boolean onCapturedPointerEvent(MotionEvent event)
{ {
int action = event.getActionMasked(); return SDLActivity.getMotionListener().onGenericMotion(this, event);
int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(i);
y = event.getY(i);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(i);
y = event.getY(i);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
}
return false;
} }
@Override @Override

View file

@ -1,4 +1,4 @@
conan export %0/../conan-skia/recipes/skia/all/conanfile.py --version=143.20251028.0 conan export %0/../conan-skia/recipes/skia/all/conanfile.py --version=143.20251028.0
conan export %0/../conan-skia/recipes/emsdk/all/conanfile.py --version=4.0.22 conan export %0/../conan-skia/recipes/emsdk/all/conanfile.py --version=4.0.22
conan export %0/../icu/all/conanfile.py --version=77.1 conan export %0/../icu/all/conanfile.py --version=77.1
conan export %0/../sdl/3.x/conanfile.py --version=3.4.8 conan export %0/../sdl/3.x/conanfile.py --version=3.4.16

View file

@ -5,6 +5,6 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
conan export "$(dirname "$0")/conan-skia/recipes/skia/all/conanfile.py" --version=143.20251028.0 conan export "$(dirname "$0")/conan-skia/recipes/skia/all/conanfile.py" --version=143.20251028.0
conan export "$(dirname "$0")/emsdk/all/conanfile.py" --version=4.0.22 conan export "$(dirname "$0")/emsdk/all/conanfile.py" --version=4.0.22
conan export "$(dirname "$0")/icu/all/conanfile.py" --version=77.1 conan export "$(dirname "$0")/icu/all/conanfile.py" --version=77.1
conan export "$(dirname "$0")/sdl/3.x/conanfile.py" --version=3.4.8 conan export "$(dirname "$0")/sdl/3.x/conanfile.py" --version=3.4.16
#conan create . -o use_system_expat=False -o use_system_freetype=False -o use_system_harfbuzz=False -o use_system_icu=False -o use_system_libjpeg_turbo=False -o use_system_libpng=False -o use_system_libwebp=False -o use_system_zlib=False --build=missing --version=134.20250327.0 #conan create . -o use_system_expat=False -o use_system_freetype=False -o use_system_harfbuzz=False -o use_system_icu=False -o use_system_libjpeg_turbo=False -o use_system_libpng=False -o use_system_libwebp=False -o use_system_zlib=False --build=missing --version=134.20250327.0
#cd ../../../.. #cd ../../../..

View file

@ -1,4 +1,4 @@
sources: sources:
"3.4.8": "3.4.16":
url: "https://github.com/libsdl-org/SDL/releases/download/release-3.4.8/SDL3-3.4.8.tar.gz" url: "https://github.com/libsdl-org/SDL/releases/download/release-3.4.16/SDL3-3.4.16.tar.gz"
sha256: "e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba" sha256: "7322236cd12090c3eb40b9728be4d49c76f66ad17d04369584d4ecad5cf77c68"

View file

@ -111,11 +111,11 @@ class CompressorRecipe(ConanFile):
self.requires("fontconfig/2.17.1") self.requires("fontconfig/2.17.1")
self.requires("egl/system") self.requires("egl/system")
elif self.settings.os == "Emscripten": elif self.settings.os == "Emscripten":
self.requires("sdl-infinipaint/3.4.8", options = { self.requires("sdl-infinipaint/3.4.16", options = {
"emscriptenPersistentPath": "/infinipaint" "emscriptenPersistentPath": "/infinipaint"
}) })
elif self.settings.os != "Android": elif self.settings.os != "Android":
self.requires("sdl-infinipaint/3.4.8") self.requires("sdl-infinipaint/3.4.16")
if self.settings.os != "Emscripten" and self.settings.os != "Macos" and self.settings.os != "Android": if self.settings.os != "Emscripten" and self.settings.os != "Macos" and self.settings.os != "Android":
self.requires("hwloc/2.12.2", options = { self.requires("hwloc/2.12.2", options = {

2
deps/SDL vendored

@ -1 +1 @@
Subproject commit d9d5536704d585616d4db3c8ba3c4ff6fc2757e1 Subproject commit fa2c02bb6e21974a89ea9824bc53c9932abe5f9c