fix: preserve Android controller fallback

This commit is contained in:
XxJSONDeruloxX committed 2026-10-25 10:38:02 -04:00
1 parent d180f563da
commit a080e4a4b4
8 files changed
+29 -7

No files matched your search

+1 -2
View File
@@ -359,8 +359,7 @@ static void refresh_status(JNIEnv *env)
SDL_free(ids);
snprintf(diag, sizeof(diag),
"Forced SDL3 USB mode: waiting for the USB driver. Plug the controller in with a USB-C cable, "
"then tap 'Force SDL3 USB driver' and allow USB access (or switch Controller source to Auto to "
"also use Bluetooth / Android-only controllers). Ignored Android-only controllers: %d", ignored);
"then tap 'Force SDL3 USB driver' and allow USB access. Android-only controllers: %d", ignored);
if (!atomic_load(&g_force))
snprintf(diag, sizeof(diag), "Waiting for a controller: plug any SDL3-supported controller in via USB-C "
"(full features) or connect it over Bluetooth");
@@ -236,7 +236,9 @@ internal fun SdlBridgeSettingsTab(host: MenuHost, track: (String) -> Modifier =
ToggleRow(host, "sdl3-driver", "SDL3 driver",
"On: SDL3 reads the controller (all its features). Off: the controller goes back to Android and DroidDeck.",
s.getBoolean("driverEnabled"), chipModifier = track("sdl3-driver")) { SdlBridgeLink.set("driverEnabled", bool = it) }
BridgeChoiceRow(host, "sdl3-source", "Controller source", "Which controllers SDL3 uses.",
if (s.getBoolean("embedded")) SettingsRow("Bluetooth and built-in controllers",
"Handled by DroidDeck's Android input. SDL3 handles USB controllers.") {}
else BridgeChoiceRow(host, "sdl3-source", "Controller source", "Which controllers SDL3 uses.",
listOf(false to "USB + Bluetooth (auto)", true to "USB only (forced)"), s.getBoolean("forceUsb"), chipModifier = track("sdl3-source")) {
SdlBridgeLink.set("forceUsb", bool = it)
}
@@ -174,7 +174,7 @@ public class BridgeActivity extends android.app.Activity implements NativeBridge
// ---- SDL3 driver ----
LinearLayout drv = card(root);
cardTitle(drv, "SDL3 driver");
addSpinner(drv, "Controller source", new String[] {
if (!Host.embedded(this)) addSpinner(drv, "Controller source", new String[] {
"Auto: any controller (SDL3 USB driver when plugged in, else Android/Bluetooth)",
"Forced: only SDL3's USB driver (USB cable only)"
}, Settings.forceUsb ? 1 : 0, v -> {
@@ -832,6 +832,7 @@ public class BridgeService extends Service implements NativeBridge.Listener {
b.putInt("presentAs", Settings.presentAs);
b.putBoolean("driverEnabled", Settings.driverEnabled);
b.putBoolean("forceUsb", Settings.forceUsb);
b.putBoolean("embedded", Host.embedded(this));
b.putInt("pollMs", Settings.pollMs);
b.putBoolean("bubble", Settings.bubble);
// choices
@@ -16,6 +16,11 @@ final class Host {
return c != null && !OWN_PACKAGE.equals(c.getPackageName());
}
static boolean usbOnly(Context c, boolean configured) {
// Android input events go to DroidDeck's activity, outside the headless SDL process.
return embedded(c) || configured;
}
/** A preferences file name of the bridge: unchanged as its own app, prefixed when built into another app. */
static String prefs(Context c, String name) {
return embedded(c) ? "sdl3bridge_" + name : name;
@@ -305,7 +305,7 @@ public final class NativeBridge {
/** Forced mode: ignore Android's basic input and only use SDL's own USB driver. */
static void applyForce() {
try { nativeSetForce(Settings.forceUsb ? 1 : 0); } catch (UnsatisfiedLinkError e) { }
try { nativeSetForce(Host.usbOnly(appContext, Settings.forceUsb) ? 1 : 0); } catch (UnsatisfiedLinkError e) { }
}
static void rescan() {
@@ -321,7 +321,7 @@ public final class NativeBridge {
try {
nativeSetDriver(Settings.driverEnabled ? 1 : 0);
nativeSetHookup(hookupMode());
if (Settings.driverEnabled) nativeSetForce(Settings.forceUsb ? 1 : 0);
if (Settings.driverEnabled) nativeSetForce(Host.usbOnly(appContext, Settings.forceUsb) ? 1 : 0);
} catch (UnsatisfiedLinkError e) { }
}
@@ -2,6 +2,7 @@ package com.example.dsedge
import android.app.Application
import android.content.ComponentName
import android.content.ContextWrapper
import android.content.pm.PackageManager
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -41,4 +42,16 @@ class EmbeddedBridgeTest {
assertEquals("sdl3bridge_settings", Host.prefs(app, "settings"))
assertEquals("sdl3bridge_engine", Host.prefs(app, "engine"))
}
@Test
fun headlessEmbeddedDriverLeavesAndroidControllersToDroidDeck() {
val app = RuntimeEnvironment.getApplication()
assertTrue(Host.usbOnly(app, false))
assertTrue(Host.usbOnly(app, true))
val standalone = object : ContextWrapper(app) {
override fun getPackageName() = Host.OWN_PACKAGE
}
assertFalse(Host.usbOnly(standalone, false))
assertTrue(Host.usbOnly(standalone, true))
}
}
+3 -1
View File
@@ -3,7 +3,9 @@
The integrated bridge runs in DroidDeck's `:sdl3` process. Open **Settings → SDL3Bridge**
or the SDL3Bridge page in the session drawer to configure up to four controllers.
USB controllers use SDL3's HID drivers; Android input remains the fallback for
Bluetooth and built-in controllers. Steam Native Hook-up forwards a USB controller's
Bluetooth and built-in controllers. The embedded driver only claims USB controllers:
Android events are delivered to DroidDeck's activity and cannot reach the separate
headless SDL process. Steam Native Hook-up forwards a USB controller's
identity and reports to Steam so Steam can use its own controller driver.
USB access requires Android's device permission. The optional floating control also