summaryrefslogtreecommitdiffstats
path: root/src/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt70
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt55
3 files changed, 54 insertions, 87 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index e96a2059b..f37875ffe 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -45,7 +45,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game
-import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.MemoryUtil
@@ -57,17 +56,16 @@ import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
- private var controllerMappingHelper: ControllerMappingHelper? = null
-
var isActivityRecreated = false
private lateinit var nfcReader: NfcReader
- private lateinit var inputHandler: InputHandler
private val gyro = FloatArray(3)
private val accel = FloatArray(3)
private var motionTimestamp: Long = 0
private var flipMotionOrientation: Boolean = false
+ private var controllerIds = InputHandler.getGameControllerIds()
+
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
private val actionMute = "ACTION_EMULATOR_MUTE"
@@ -95,8 +93,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
isActivityRecreated = savedInstanceState != null
- controllerMappingHelper = ControllerMappingHelper()
-
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive()
@@ -105,8 +101,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader = NfcReader(this)
nfcReader.initialize()
- inputHandler = InputHandler()
- inputHandler.initialize()
+ InputHandler.initialize()
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
@@ -162,6 +157,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
+ InputHandler.updateControllerIds()
buildPictureInPictureParams()
}
@@ -195,7 +191,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super.dispatchKeyEvent(event)
}
- return inputHandler.dispatchKeyEvent(event)
+ return InputHandler.dispatchKeyEvent(event)
}
override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
@@ -210,7 +206,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return true
}
- return inputHandler.dispatchGenericMotionEvent(event)
+ return InputHandler.dispatchGenericMotionEvent(event)
}
override fun onSensorChanged(event: SensorEvent) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
deleted file mode 100644
index eeefcdf20..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.utils
-
-import android.view.InputDevice
-import android.view.KeyEvent
-import android.view.MotionEvent
-
-/**
- * Some controllers have incorrect mappings. This class has special-case fixes for them.
- */
-class ControllerMappingHelper {
- /**
- * Some controllers report extra button presses that can be ignored.
- */
- fun shouldKeyBeIgnored(inputDevice: InputDevice, keyCode: Int): Boolean {
- return if (isDualShock4(inputDevice)) {
- // The two analog triggers generate analog motion events as well as a keycode.
- // We always prefer to use the analog values, so throw away the button press
- keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
- } else {
- false
- }
- }
-
- /**
- * Scale an axis to be zero-centered with a proper range.
- */
- fun scaleAxis(inputDevice: InputDevice, axis: Int, value: Float): Float {
- if (isDualShock4(inputDevice)) {
- // Android doesn't have correct mappings for this controller's triggers. It reports them
- // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0]
- // Scale them to properly zero-centered with a range of [0.0, 1.0].
- if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) {
- return (value + 1) / 2.0f
- }
- } else if (isXboxOneWireless(inputDevice)) {
- // Same as the DualShock 4, the mappings are missing.
- if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) {
- return (value + 1) / 2.0f
- }
- if (axis == MotionEvent.AXIS_GENERIC_1) {
- // This axis is stuck at ~.5. Ignore it.
- return 0.0f
- }
- } else if (isMogaPro2Hid(inputDevice)) {
- // This controller has a broken axis that reports a constant value. Ignore it.
- if (axis == MotionEvent.AXIS_GENERIC_1) {
- return 0.0f
- }
- }
- return value
- }
-
- // Sony DualShock 4 controller
- private fun isDualShock4(inputDevice: InputDevice): Boolean {
- return inputDevice.vendorId == 0x54c && inputDevice.productId == 0x9cc
- }
-
- // Microsoft Xbox One controller
- private fun isXboxOneWireless(inputDevice: InputDevice): Boolean {
- return inputDevice.vendorId == 0x45e && inputDevice.productId == 0x2e0
- }
-
- // Moga Pro 2 HID
- private fun isMogaPro2Hid(inputDevice: InputDevice): Boolean {
- return inputDevice.vendorId == 0x20d6 && inputDevice.productId == 0x6271
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index e963dfbc1..fc6a8b5cb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -3,17 +3,24 @@
package org.yuzu.yuzu_emu.utils
+import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary
-class InputHandler {
+object InputHandler {
+ private var controllerIds = getGameControllerIds()
+
fun initialize() {
// Connect first controller
NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device))
}
+ fun updateControllerIds() {
+ controllerIds = getGameControllerIds()
+ }
+
fun dispatchKeyEvent(event: KeyEvent): Boolean {
val button: Int = when (event.device.vendorId) {
0x045E -> getInputXboxButtonKey(event.keyCode)
@@ -35,7 +42,7 @@ class InputHandler {
}
return NativeLibrary.onGamePadButtonEvent(
- getPlayerNumber(event.device.controllerNumber),
+ getPlayerNumber(event.device.controllerNumber, event.deviceId),
button,
action
)
@@ -58,9 +65,14 @@ class InputHandler {
return true
}
- private fun getPlayerNumber(index: Int): Int {
+ private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int {
+ var deviceIndex = index
+ if (deviceId != -1) {
+ deviceIndex = controllerIds[deviceId]!!
+ }
+
// TODO: Joycons are handled as different controllers. Find a way to merge them.
- return when (index) {
+ return when (deviceIndex) {
2 -> NativeLibrary.Player2Device
3 -> NativeLibrary.Player3Device
4 -> NativeLibrary.Player4Device
@@ -238,7 +250,7 @@ class InputHandler {
}
private fun setGenericAxisInput(event: MotionEvent, axis: Int) {
- val playerNumber = getPlayerNumber(event.device.controllerNumber)
+ val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
when (axis) {
MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
@@ -297,7 +309,7 @@ class InputHandler {
private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
// Joycon support is half dead. Right joystick doesn't work
- val playerNumber = getPlayerNumber(event.device.controllerNumber)
+ val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
when (axis) {
MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
@@ -325,7 +337,7 @@ class InputHandler {
}
private fun setRazerAxisInput(event: MotionEvent, axis: Int) {
- val playerNumber = getPlayerNumber(event.device.controllerNumber)
+ val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
when (axis) {
MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
@@ -362,4 +374,33 @@ class InputHandler {
)
}
}
+
+ fun getGameControllerIds(): Map<Int, Int> {
+ val gameControllerDeviceIds = mutableMapOf<Int, Int>()
+ val deviceIds = InputDevice.getDeviceIds()
+ var controllerSlot = 1
+ deviceIds.forEach { deviceId ->
+ InputDevice.getDevice(deviceId)?.apply {
+ // Don't over-assign controllers
+ if (controllerSlot >= 8) {
+ return gameControllerDeviceIds
+ }
+
+ // Verify that the device has gamepad buttons, control sticks, or both.
+ if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD ||
+ sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
+ ) {
+ // This device is a game controller. Store its device ID.
+ if (deviceId and id and vendorId and productId != 0) {
+ // Additionally filter out devices that have no ID
+ gameControllerDeviceIds
+ .takeIf { !it.contains(deviceId) }
+ ?.put(deviceId, controllerSlot)
+ controllerSlot++
+ }
+ }
+ }
+ }
+ return gameControllerDeviceIds
+ }
}