diff --git a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt index 93f4c4c53..874ab2a54 100644 --- a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt +++ b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt @@ -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 diff --git a/app/src/main/java/com/droiddeck/launcher/input/PadBridge.java b/app/src/main/java/com/droiddeck/launcher/input/PadBridge.java index bdc3ff173..a626d8600 100644 --- a/app/src/main/java/com/droiddeck/launcher/input/PadBridge.java +++ b/app/src/main/java/com/droiddeck/launcher/input/PadBridge.java @@ -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 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; diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt index 54d81f27b..8560327ea 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt @@ -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 diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt index 9dde69517..3c0c6f230 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt @@ -709,17 +709,21 @@ class SessionService : Service() { private fun addControllerEnvironment(guest: MutableList, 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 { diff --git a/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt b/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt index f5d649025..a0cbfa1a7 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt @@ -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( diff --git a/app/src/main/java/com/droiddeck/launcher/ui/SecondScreenControls.kt b/app/src/main/java/com/droiddeck/launcher/ui/SecondScreenControls.kt index 908b00e0a..a7a757ebf 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/SecondScreenControls.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/SecondScreenControls.kt @@ -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())