feat: let the built-in controller be turned off (#1207)

This commit is contained in:
kingokksa authored and GitHub committed 2026-10-21 19:49:31 -04:00
1 parent ed29f77fc2
commit 2b0c55f72e
6 files changed
+48 -8

No files matched your search

@@ -475,6 +475,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
val bridge = PadBridge(File(LinuxRuntime.sessionRoot(this), "dev/input"))
bridge.setSelectSteam(ControllerPrefs.read(this).selectSteam)
bridge.setBuiltinPad(SessionState.mode != SessionService.MODE_STEAM || SessionPrefs.builtinPad(this))
padBridge = bridge
SessionState.padBridge = bridge
if (Build.VERSION.SDK_INT >= 31) {
@@ -2747,7 +2748,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
?.let { FileUtils.readString(it)?.trim()?.lowercase() }
?: SessionPrefs.oscMode(this)
val controls = onScreenControls ?: return
if (pipUi || unfoldedPanel != null || pcKeyboardOpen) {
if (pipUi || unfoldedPanel != null || pcKeyboardOpen ||
(SessionState.mode == SessionService.MODE_STEAM && !SessionPrefs.builtinPad(this))) {
onScreenButtonsVisible = false
controls.releaseAll()
controls.visibility = View.GONE
@@ -55,6 +55,7 @@ public final class PadBridge {
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private boolean open;
private volatile boolean selectSteam;
private volatile boolean builtinPad = true;
private TouchRoute touchRoute;
private final PadState routedTouch = new PadState();
private boolean systemGuidePressed;
@@ -110,6 +111,25 @@ public final class PadBridge {
excluded = devices;
}
public synchronized void setBuiltinPad(boolean on) {
boolean changed = builtinPad != on;
builtinPad = on;
if (changed && !on) resetBuiltin();
}
private void resetBuiltin() {
routedTouch.clear();
systemGuidePressed = false;
systemQamPressed = false;
qamTapPressed = false;
qamChordActive = false;
chordButtonPressed = false;
qamChordGeneration++;
chordHandler.removeCallbacksAndMessages(null);
state.clear();
publish();
}
public synchronized boolean start() {
if (!open) {
open = writer.open();
@@ -373,6 +393,7 @@ public final class PadBridge {
}
public synchronized void applyTouch(java.util.function.Consumer<PadState> mutation) {
if (!builtinPad) return;
if (touchRoute != null && touchRoute.active()) {
mutation.accept(routedTouch);
touchRoute.accept(routedTouch);
@@ -412,6 +433,7 @@ public final class PadBridge {
}
public synchronized void setSystemButtons(boolean guidePressed, boolean qamPressed) {
if (!builtinPad) return;
if (systemGuidePressed == guidePressed && systemQamPressed == qamPressed) return;
boolean qamStarted = qamPressed && !systemQamPressed;
systemGuidePressed = guidePressed;
@@ -443,6 +465,7 @@ public final class PadBridge {
}
public synchronized void triggerQam() {
if (!builtinPad) return;
if (!nativeQam()) {
startChord(PadState.A);
return;
@@ -461,6 +484,7 @@ public final class PadBridge {
}
public synchronized void triggerSteamKeyboard() {
if (!builtinPad) return;
startChord(PadState.X);
}
@@ -632,6 +656,7 @@ public final class PadBridge {
}
private void publish() {
if (!builtinPad && !open && owners[0] == NO_CONTROLLER && partners[0] == NO_CONTROLLER) return;
if (!open && !start()) {
statDropped++;
return;
@@ -28,6 +28,7 @@ object SessionPrefs {
const val CONTROLLER_STEAM = "steamcontroller"
const val CONTROLLER_DS4 = "dualshock4"
const val CONTROLLER_DUALSENSE = "dualsense"
const val CONTROLLER_NONE = "none"
const val OSC_AUTO = "auto"
const val OSC_ALWAYS = "always"
const val OSC_STEAM_QAM = "steam-qam"
@@ -458,6 +459,8 @@ object SessionPrefs {
fun steamController(context: Context): String =
prefs(context).getString("steamController", CONTROLLER_DECK) ?: CONTROLLER_DECK
fun setSteamController(context: Context, id: String) { prefs(context).edit().putString("steamController", id).apply() }
fun builtinPad(context: Context): Boolean = steamController(context) != CONTROLLER_NONE
fun padType(id: String): Int = when (id) {
CONTROLLER_STEAM -> com.droiddeck.launcher.input.FakeInputWriter.PAD_TYPE_STEAM
CONTROLLER_XBOX360 -> com.droiddeck.launcher.input.FakeInputWriter.PAD_TYPE_XBOX
@@ -709,17 +709,21 @@ class SessionService : Service() {
private fun addControllerEnvironment(guest: MutableList<String>, fakeInputDir: File, sessionDir: File) {
FakeInputWriter.prepareRingSlots(fakeInputDir, 4)
val builtinPad = SessionPrefs.builtinPad(this)
val hiddenPad = SessionState.mode == MODE_STEAM && !builtinPad
fakeInputDir.listFiles()?.forEach { file ->
val node = Regex("event(\\d+)").matchEntire(file.name)?.groupValues?.get(1)?.toIntOrNull()
if (file.name.startsWith(".uinput-") || (node != null && node >= FIRST_VIRTUAL_PAD)) file.delete()
if (file.name.startsWith(".uinput-") || (node != null && node >= FIRST_VIRTUAL_PAD) ||
(hiddenPad && node == 0)) file.delete()
}
guest.add("FAKE_EVDEV_DIR=" + fakeInputDir.path)
val rings = FakeInputWriter.getRingEnv(fakeInputDir)
if (!rings.isNullOrEmpty()) guest.add("FAKE_EVDEV_MEMFD_PATHS=$rings")
val presented = if (hiddenPad) rings?.split(';')?.filterNot { it.startsWith("0=") }?.joinToString(";") else rings
if (!presented.isNullOrEmpty()) guest.add("FAKE_EVDEV_MEMFD_PATHS=$presented")
guest.add("FAKE_EVDEV_IDENTITY=xbox360")
guest.add("FAKE_EVDEV_VIBRATION=1")
val uinput = !File(Environment.getExternalStorageDirectory(), NO_UINPUT_SWITCH).exists()
val wantsDeck = uinput && SessionState.mode == MODE_STEAM &&
val wantsDeck = uinput && builtinPad && SessionState.mode == MODE_STEAM &&
SessionState.steamUi != "desktop" &&
!File(Environment.getExternalStorageDirectory(), NO_DECK_PAD_SWITCH).exists()
val wantsTouch = wantsDeck && !File(Environment.getExternalStorageDirectory(), NO_STEAM_TOUCH_SWITCH).exists()
@@ -1740,7 +1744,9 @@ class SessionService : Service() {
pads.add(String.format(java.util.Locale.ROOT, "\"%s\" (%04x:%04x)", d.name, d.vendorId, d.productId))
}
Log.i(TAG, "controllers at start: " + (if (pads.isEmpty()) "none" else pads.joinToString(", ")) +
"; presented to the guest as " + if (SessionState.deckPad) "switchable, starting as " + SessionPrefs.steamController(this) else "an Xbox 360 controller")
"; presented to the guest as " + if (SessionState.deckPad) "switchable, starting as " + SessionPrefs.steamController(this)
else if (SessionState.mode == MODE_STEAM && !SessionPrefs.builtinPad(this)) "one per connected controller, no built-in pad"
else "an Xbox 360 controller")
}
companion object {
@@ -276,8 +276,10 @@ fun ModeSheetRows(s: ModeSettings, a: ModeSettingsActions, tab: String, extras:
SessionPrefs.CONTROLLER_DS4 to stringResource(R.string.mode_controller_ds4),
SessionPrefs.CONTROLLER_DUALSENSE to stringResource(R.string.mode_controller_dualsense),
SessionPrefs.CONTROLLER_XBOX360 to stringResource(R.string.mode_controller_x360),
SessionPrefs.CONTROLLER_NONE to stringResource(R.string.second_mode_none),
), s.steamController,
note = stringResource(R.string.mode_controller_note),
note = if (s.steamController == SessionPrefs.CONTROLLER_NONE) stringResource(R.string.common_applies_next_session)
else stringResource(R.string.mode_controller_note),
onPick = a.onSteamController,
)
if (steam && s.unfoldedControls != null) ToggleRow(
@@ -82,9 +82,11 @@ internal class SecondScreenControls(
keyboardHost = null
closeTerminal()
root.removeAllViews()
if (currentMode == SecondScreenMode.NONE) return
val mode = if (currentMode == SecondScreenMode.DECK_CONTROLS && !SessionState.deckPad) SecondScreenMode.NONE
else currentMode
if (mode == SecondScreenMode.NONE) return
when (currentMode) {
when (mode) {
SecondScreenMode.KEYBOARD_TRACKPAD -> renderKeyboardAndTrackpad()
SecondScreenMode.TERMINAL -> renderTerminal()
SecondScreenMode.DECK_CONTROLS -> root.addView(DeckControlsPanel(context, onClose, onSteamMenu, onQam, pad), weightParams())