diff --git a/README.md b/README.md index 635b8c524..6f5cb8ba8 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3956. +This is the source code for early-access 3957. ## Legal Notice 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 100755 --- 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/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index e963dfbc1..fc6a8b5cb 100755 --- 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 { + val gameControllerDeviceIds = mutableMapOf() + 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 + } } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3c7dde361..c73bcb0e8 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3484,7 +3484,7 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { } void GMainWindow::OnExit() { - OnStopGame(); + ShutdownGame(); } void GMainWindow::OnSaveConfig() {