mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 19:41:10 +00:00
fix: hold Home as Steam on Retroid Nova pads (#1674)
* fix: hold Home as Steam on Retroid Nova pads * fix(input): track Home Guide apart from controller Guide * fix(input): keep held controls on Home cancel and resume on Home
This commit is contained in:
1 parent
b5cc6dd185
commit
e8bfe0c4e4
6 files changed
+111
-12
No files matched your search
@@ -68,6 +68,7 @@ import com.droiddeck.launcher.gpu.ScreenEffects
|
||||
import com.droiddeck.launcher.gpu.Lossless
|
||||
import com.droiddeck.launcher.gpu.TurnipDriver
|
||||
import com.droiddeck.launcher.input.EvdevKeys
|
||||
import com.droiddeck.launcher.input.HomeGuideInput
|
||||
import com.droiddeck.launcher.input.InputSpace
|
||||
import com.droiddeck.launcher.input.KeyboardHost
|
||||
import com.droiddeck.launcher.input.ControllerPrefs
|
||||
@@ -1330,7 +1331,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
if (incoming?.action != SessionService.ACTION_HOME_GUIDE) return
|
||||
incoming.action = null
|
||||
setIntent(incoming)
|
||||
if (SessionState.running && SessionState.mode == SessionService.MODE_STEAM) sendSteamGuide()
|
||||
if (SessionState.suspended) SessionService.resume(this)
|
||||
else if (SessionState.running && SessionState.mode == SessionService.MODE_STEAM) sendSteamGuide()
|
||||
}
|
||||
|
||||
|
||||
@@ -2276,7 +2278,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
return true
|
||||
}
|
||||
val resumeKey = event.keyCode == KeyEvent.KEYCODE_BUTTON_A || event.keyCode == KeyEvent.KEYCODE_BUTTON_START ||
|
||||
event.keyCode == KeyEvent.KEYCODE_BUTTON_MODE || event.keyCode == KeyEvent.KEYCODE_HOME
|
||||
event.keyCode == KeyEvent.KEYCODE_BUTTON_MODE || HomeGuideInput.isHome(event.keyCode)
|
||||
val resumeKeyId = event.deviceId to event.keyCode
|
||||
if (fromController && resumeKey && (SessionState.suspended || resumeKeyId in resumeKeysDown)) {
|
||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||
@@ -3290,7 +3292,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
enabled = {
|
||||
resumed && window.decorView.hasWindowFocus() && !ended && !drawerOpen && !pcKeyboardOpen &&
|
||||
!achievementsOpen && !gestureSettingsOpen && !pipUi && SessionState.running && !SessionState.suspended &&
|
||||
SessionState.mode == SessionService.MODE_STEAM && padBridge?.canHoldGuide() == true
|
||||
SessionState.mode == SessionService.MODE_STEAM && padBridge != null
|
||||
},
|
||||
setPressed = { padBridge?.setHomeGuidePressed(it) },
|
||||
onCancel = { padBridge?.cancelHomeGuide() },
|
||||
|
||||
@@ -38,7 +38,7 @@ object HomeButtonShortcuts {
|
||||
|
||||
fun onKeyEvent(context: Context, event: KeyEvent): Boolean {
|
||||
// AYN maps its system-assistant key (gpio-keys KEY_F24) to HOME too; only the pad's Home is Guide.
|
||||
if (event.keyCode != KeyEvent.KEYCODE_HOME || !PadBridge.isControllerKeyEvent(event)) return false
|
||||
if (!HomeGuideInput.isHome(event.keyCode) || !PadBridge.isControllerKeyEvent(event)) return false
|
||||
return input.onKeyEvent(event, enabled() && HomeApp.isDefault(context))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ class HomeGuideInput(private val setPressed: (Boolean) -> Unit, private val onCa
|
||||
private val cancelled = mutableSetOf<Int>()
|
||||
|
||||
fun onKeyEvent(event: KeyEvent, enabled: Boolean): Boolean {
|
||||
if (event.keyCode != KeyEvent.KEYCODE_HOME) return false
|
||||
if (!isHome(event.keyCode)) return false
|
||||
if (!enabled) cancel()
|
||||
val device = event.deviceId
|
||||
return when (event.action) {
|
||||
@@ -39,4 +39,9 @@ class HomeGuideInput(private val setPressed: (Boolean) -> Unit, private val onCa
|
||||
cancelled.remove(device)
|
||||
if (presses.remove(device) && presses.isEmpty()) { onCancel(); setPressed(false) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// The Retroid Nova pad falls back to Generic.kl, which maps its Home scan code to MOVE_HOME.
|
||||
fun isHome(keyCode: Int) = keyCode == KeyEvent.KEYCODE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_HOME
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ public final class PadBridge {
|
||||
private final FakeInputWriter[] playerWriters = new FakeInputWriter[SLOTS];
|
||||
private final PadState[] playerStates = new PadState[SLOTS];
|
||||
private final PadState effectiveState = new PadState();
|
||||
private final PadState homeGuideReport = new PadState();
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean open;
|
||||
private volatile boolean selectSteam;
|
||||
@@ -63,6 +64,7 @@ public final class PadBridge {
|
||||
private boolean systemQamPressed;
|
||||
private boolean backQamPressed;
|
||||
private boolean homeGuidePressed;
|
||||
private int homeGuideSlot = -1;
|
||||
private final PadState lastBackQamReport = new PadState();
|
||||
private final PadState lastWrittenReport = new PadState();
|
||||
private boolean backQamAxesBlocked;
|
||||
@@ -162,6 +164,7 @@ public final class PadBridge {
|
||||
|
||||
public synchronized void stop() {
|
||||
homeGuidePressed = false;
|
||||
homeGuideSlot = -1;
|
||||
clearBackQamWrites();
|
||||
releaseBackPaddles();
|
||||
activeControllerId = NO_CONTROLLER;
|
||||
@@ -229,6 +232,7 @@ public final class PadBridge {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_HOME:
|
||||
case KeyEvent.KEYCODE_MOVE_HOME:
|
||||
if (!isFromController(device)) return false;
|
||||
return device.supportsSource(InputDevice.SOURCE_GAMEPAD)
|
||||
|| device.getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC
|
||||
@@ -329,7 +333,8 @@ public final class PadBridge {
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL: pad.press(8, pressed); break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR: pad.press(9, pressed); break;
|
||||
case KeyEvent.KEYCODE_BUTTON_MODE:
|
||||
case KeyEvent.KEYCODE_HOME: pad.press(PadState.GUIDE, pressed); break;
|
||||
case KeyEvent.KEYCODE_HOME:
|
||||
case KeyEvent.KEYCODE_MOVE_HOME: pad.press(PadState.GUIDE, pressed); break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Z:
|
||||
if (isPrimary(event)) DeckControls.INSTANCE.setGrip(isNovaController(event.getDevice()) ? DeckControls.R4 : DeckControls.L4, pressed);
|
||||
break;
|
||||
@@ -485,6 +490,7 @@ public final class PadBridge {
|
||||
|
||||
public synchronized void releaseAll() {
|
||||
homeGuidePressed = false;
|
||||
homeGuideSlot = -1;
|
||||
clearBackQamWrites();
|
||||
routedTouch.clear();
|
||||
backQamPressed = false;
|
||||
@@ -512,18 +518,24 @@ public final class PadBridge {
|
||||
return builtinPad && nativeQam();
|
||||
}
|
||||
|
||||
public boolean canHoldGuide() {
|
||||
return builtinPad;
|
||||
}
|
||||
|
||||
public synchronized void setHomeGuidePressed(boolean pressed) {
|
||||
if (pressed && !canHoldGuide()) return;
|
||||
int slot = pressed ? firstPlayerSlot() : homeGuideSlot;
|
||||
if (slot > 0) {
|
||||
homeGuideSlot = pressed ? slot : -1;
|
||||
publish(slot);
|
||||
return;
|
||||
}
|
||||
if (homeGuidePressed == pressed) return;
|
||||
homeGuidePressed = pressed;
|
||||
updateHeldModifier(PadState.GUIDE, pressed);
|
||||
}
|
||||
|
||||
public synchronized void cancelHomeGuide() {
|
||||
int slot = homeGuideSlot;
|
||||
if (slot > 0) {
|
||||
homeGuideSlot = -1;
|
||||
publish(slot);
|
||||
}
|
||||
if (!homeGuidePressed) return;
|
||||
clearBackQamWrites();
|
||||
homeGuidePressed = false;
|
||||
@@ -784,6 +796,7 @@ public final class PadBridge {
|
||||
partners[slot] = NO_CONTROLLER;
|
||||
clearJoyCons(slot);
|
||||
slotControllers.set(slot, NO_CONTROLLER);
|
||||
if (slot == homeGuideSlot) homeGuideSlot = -1;
|
||||
if (slot == firstPlayerSlot()) releaseBackPaddles();
|
||||
if (slot == 0) {
|
||||
state.clear();
|
||||
@@ -840,7 +853,13 @@ public final class PadBridge {
|
||||
}
|
||||
playerWriters[slot] = player;
|
||||
}
|
||||
player.writePad(playerStates[slot]);
|
||||
PadState report = playerStates[slot];
|
||||
if (slot == homeGuideSlot) {
|
||||
homeGuideReport.copyFrom(report);
|
||||
homeGuideReport.press(PadState.GUIDE, true);
|
||||
report = homeGuideReport;
|
||||
}
|
||||
player.writePad(report);
|
||||
}
|
||||
|
||||
private void publish() {
|
||||
|
||||
@@ -27,6 +27,14 @@ class HomeGuideInputTest {
|
||||
assertEquals(listOf(true, false), states)
|
||||
}
|
||||
|
||||
@Test fun genericLayoutMoveHomeHoldsGuideLikeHome() {
|
||||
val down = KeyEvent(10, 110, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_HOME, 0, 0, 10, 102)
|
||||
val up = KeyEvent(10, 200, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MOVE_HOME, 0, 0, 10, 102)
|
||||
assertTrue(input.onKeyEvent(down, true))
|
||||
assertTrue(input.onKeyEvent(up, true))
|
||||
assertEquals(listOf(true, false), states)
|
||||
}
|
||||
|
||||
@Test fun anUnownedHomePressKeepsAndroidBehaviorEvenIfSessionBecomesEligibleBeforeRelease() {
|
||||
assertFalse(input.onKeyEvent(home(KeyEvent.ACTION_DOWN), false))
|
||||
assertFalse(input.onKeyEvent(home(KeyEvent.ACTION_UP), true))
|
||||
|
||||
@@ -343,6 +343,71 @@ class PadBridgeQamTest {
|
||||
assertFalse(pad().isDown(PadState.X))
|
||||
}
|
||||
|
||||
@Test fun homeGuideHoldsOnThePrimaryPlayerWithoutABuiltinPad() {
|
||||
PadBridge.setFirstSlot(1)
|
||||
try {
|
||||
bridge.setBuiltinPad(false)
|
||||
bridge.setHomeGuidePressed(true)
|
||||
assertTrue(requireNotNull(JoyConPadTest.RecordingWriter.pads[1]).isDown(PadState.GUIDE))
|
||||
bridge.setHomeGuidePressed(false)
|
||||
assertFalse(requireNotNull(JoyConPadTest.RecordingWriter.pads[1]).isDown(PadState.GUIDE))
|
||||
} finally {
|
||||
PadBridge.setFirstSlot(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun homeGuideAndControllerGuideReleaseIndependentlyOnThePrimaryPlayer() {
|
||||
PadBridge.setFirstSlot(1)
|
||||
try {
|
||||
bridge.setBuiltinPad(false)
|
||||
val device = controller()
|
||||
bridge.onKeyEvent(key(device, KeyEvent.KEYCODE_BUTTON_MODE))
|
||||
bridge.setHomeGuidePressed(true)
|
||||
bridge.onKeyEvent(key(device, KeyEvent.KEYCODE_BUTTON_MODE, KeyEvent.ACTION_UP))
|
||||
assertTrue(requireNotNull(JoyConPadTest.RecordingWriter.pads[1]).isDown(PadState.GUIDE))
|
||||
bridge.onKeyEvent(key(device, KeyEvent.KEYCODE_BUTTON_MODE))
|
||||
bridge.setHomeGuidePressed(false)
|
||||
assertTrue(requireNotNull(JoyConPadTest.RecordingWriter.pads[1]).isDown(PadState.GUIDE))
|
||||
bridge.onKeyEvent(key(device, KeyEvent.KEYCODE_BUTTON_MODE, KeyEvent.ACTION_UP))
|
||||
assertFalse(requireNotNull(JoyConPadTest.RecordingWriter.pads[1]).isDown(PadState.GUIDE))
|
||||
} finally {
|
||||
PadBridge.setFirstSlot(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun removingThePrimaryPlayerForgetsItsHomeGuide() {
|
||||
PadBridge.setFirstSlot(1)
|
||||
try {
|
||||
bridge.setBuiltinPad(false)
|
||||
val device = controller()
|
||||
bridge.onKeyEvent(key(device, KeyEvent.KEYCODE_BUTTON_A, KeyEvent.ACTION_UP))
|
||||
bridge.setHomeGuidePressed(true)
|
||||
bridge.onDeviceRemoved(device.id)
|
||||
JoyConPadTest.RecordingWriter.pads.remove(1)
|
||||
bridge.cancelHomeGuide()
|
||||
bridge.setHomeGuidePressed(false)
|
||||
assertNull(JoyConPadTest.RecordingWriter.pads[1])
|
||||
} finally {
|
||||
PadBridge.setFirstSlot(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun cancellingHomeGuideOnAPlayerSlotKeepsItsHeldControls() {
|
||||
PadBridge.setFirstSlot(1)
|
||||
try {
|
||||
bridge.setBuiltinPad(false)
|
||||
val device = controller()
|
||||
bridge.onKeyEvent(key(device, KeyEvent.KEYCODE_BUTTON_A))
|
||||
bridge.setHomeGuidePressed(true)
|
||||
bridge.cancelHomeGuide()
|
||||
val pad = requireNotNull(JoyConPadTest.RecordingWriter.pads[1])
|
||||
assertFalse(pad.isDown(PadState.GUIDE))
|
||||
assertTrue(pad.isDown(PadState.A))
|
||||
} finally {
|
||||
PadBridge.setFirstSlot(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun cancellingHomeDiscardsQueuedPartnersAndReleasesGuideImmediately() {
|
||||
bridge.setHomeGuidePressed(true)
|
||||
bridge.applyExternal({ it.press(PadState.LB, true); it.leftY = -1f }, true)
|
||||
|
||||
Reference in new issue
Block a user