mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 18:54:14 +01:00 
			
		
		
		
	Merge pull request #11925 from t895/controller-fix
android: Fix controllers stuck on player 2
This commit is contained in:
		
						commit
						ab3e3c11af
					
				| @ -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) { | ||||
|  | ||||
| @ -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 | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user