mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 10:44:49 +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.features.settings.model.Settings | ||||||
| import org.yuzu.yuzu_emu.model.EmulationViewModel | import org.yuzu.yuzu_emu.model.EmulationViewModel | ||||||
| import org.yuzu.yuzu_emu.model.Game | 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.ForegroundService | ||||||
| import org.yuzu.yuzu_emu.utils.InputHandler | import org.yuzu.yuzu_emu.utils.InputHandler | ||||||
| import org.yuzu.yuzu_emu.utils.MemoryUtil | import org.yuzu.yuzu_emu.utils.MemoryUtil | ||||||
| @ -57,17 +56,16 @@ import kotlin.math.roundToInt | |||||||
| class EmulationActivity : AppCompatActivity(), SensorEventListener { | class EmulationActivity : AppCompatActivity(), SensorEventListener { | ||||||
|     private lateinit var binding: ActivityEmulationBinding |     private lateinit var binding: ActivityEmulationBinding | ||||||
| 
 | 
 | ||||||
|     private var controllerMappingHelper: ControllerMappingHelper? = null |  | ||||||
| 
 |  | ||||||
|     var isActivityRecreated = false |     var isActivityRecreated = false | ||||||
|     private lateinit var nfcReader: NfcReader |     private lateinit var nfcReader: NfcReader | ||||||
|     private lateinit var inputHandler: InputHandler |  | ||||||
| 
 | 
 | ||||||
|     private val gyro = FloatArray(3) |     private val gyro = FloatArray(3) | ||||||
|     private val accel = FloatArray(3) |     private val accel = FloatArray(3) | ||||||
|     private var motionTimestamp: Long = 0 |     private var motionTimestamp: Long = 0 | ||||||
|     private var flipMotionOrientation: Boolean = false |     private var flipMotionOrientation: Boolean = false | ||||||
| 
 | 
 | ||||||
|  |     private var controllerIds = InputHandler.getGameControllerIds() | ||||||
|  | 
 | ||||||
|     private val actionPause = "ACTION_EMULATOR_PAUSE" |     private val actionPause = "ACTION_EMULATOR_PAUSE" | ||||||
|     private val actionPlay = "ACTION_EMULATOR_PLAY" |     private val actionPlay = "ACTION_EMULATOR_PLAY" | ||||||
|     private val actionMute = "ACTION_EMULATOR_MUTE" |     private val actionMute = "ACTION_EMULATOR_MUTE" | ||||||
| @ -95,8 +93,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||||||
| 
 | 
 | ||||||
|         isActivityRecreated = savedInstanceState != null |         isActivityRecreated = savedInstanceState != null | ||||||
| 
 | 
 | ||||||
|         controllerMappingHelper = ControllerMappingHelper() |  | ||||||
| 
 |  | ||||||
|         // Set these options now so that the SurfaceView the game renders into is the right size. |         // Set these options now so that the SurfaceView the game renders into is the right size. | ||||||
|         enableFullscreenImmersive() |         enableFullscreenImmersive() | ||||||
| 
 | 
 | ||||||
| @ -105,8 +101,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||||||
|         nfcReader = NfcReader(this) |         nfcReader = NfcReader(this) | ||||||
|         nfcReader.initialize() |         nfcReader.initialize() | ||||||
| 
 | 
 | ||||||
|         inputHandler = InputHandler() |         InputHandler.initialize() | ||||||
|         inputHandler.initialize() |  | ||||||
| 
 | 
 | ||||||
|         val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |         val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||||
|         if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { |         if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { | ||||||
| @ -162,6 +157,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||||||
|         super.onResume() |         super.onResume() | ||||||
|         nfcReader.startScanning() |         nfcReader.startScanning() | ||||||
|         startMotionSensorListener() |         startMotionSensorListener() | ||||||
|  |         InputHandler.updateControllerIds() | ||||||
| 
 | 
 | ||||||
|         buildPictureInPictureParams() |         buildPictureInPictureParams() | ||||||
|     } |     } | ||||||
| @ -195,7 +191,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||||||
|             return super.dispatchKeyEvent(event) |             return super.dispatchKeyEvent(event) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return inputHandler.dispatchKeyEvent(event) |         return InputHandler.dispatchKeyEvent(event) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { |     override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { | ||||||
| @ -210,7 +206,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||||||
|             return true |             return true | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return inputHandler.dispatchGenericMotionEvent(event) |         return InputHandler.dispatchGenericMotionEvent(event) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun onSensorChanged(event: SensorEvent) { |     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 | package org.yuzu.yuzu_emu.utils | ||||||
| 
 | 
 | ||||||
|  | import android.view.InputDevice | ||||||
| import android.view.KeyEvent | import android.view.KeyEvent | ||||||
| import android.view.MotionEvent | import android.view.MotionEvent | ||||||
| import kotlin.math.sqrt | import kotlin.math.sqrt | ||||||
| import org.yuzu.yuzu_emu.NativeLibrary | import org.yuzu.yuzu_emu.NativeLibrary | ||||||
| 
 | 
 | ||||||
| class InputHandler { | object InputHandler { | ||||||
|  |     private var controllerIds = getGameControllerIds() | ||||||
|  | 
 | ||||||
|     fun initialize() { |     fun initialize() { | ||||||
|         // Connect first controller |         // Connect first controller | ||||||
|         NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)) |         NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fun updateControllerIds() { | ||||||
|  |         controllerIds = getGameControllerIds() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fun dispatchKeyEvent(event: KeyEvent): Boolean { |     fun dispatchKeyEvent(event: KeyEvent): Boolean { | ||||||
|         val button: Int = when (event.device.vendorId) { |         val button: Int = when (event.device.vendorId) { | ||||||
|             0x045E -> getInputXboxButtonKey(event.keyCode) |             0x045E -> getInputXboxButtonKey(event.keyCode) | ||||||
| @ -35,7 +42,7 @@ class InputHandler { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return NativeLibrary.onGamePadButtonEvent( |         return NativeLibrary.onGamePadButtonEvent( | ||||||
|             getPlayerNumber(event.device.controllerNumber), |             getPlayerNumber(event.device.controllerNumber, event.deviceId), | ||||||
|             button, |             button, | ||||||
|             action |             action | ||||||
|         ) |         ) | ||||||
| @ -58,9 +65,14 @@ class InputHandler { | |||||||
|         return true |         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. |         // TODO: Joycons are handled as different controllers. Find a way to merge them. | ||||||
|         return when (index) { |         return when (deviceIndex) { | ||||||
|             2 -> NativeLibrary.Player2Device |             2 -> NativeLibrary.Player2Device | ||||||
|             3 -> NativeLibrary.Player3Device |             3 -> NativeLibrary.Player3Device | ||||||
|             4 -> NativeLibrary.Player4Device |             4 -> NativeLibrary.Player4Device | ||||||
| @ -238,7 +250,7 @@ class InputHandler { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun setGenericAxisInput(event: MotionEvent, axis: Int) { |     private fun setGenericAxisInput(event: MotionEvent, axis: Int) { | ||||||
|         val playerNumber = getPlayerNumber(event.device.controllerNumber) |         val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) | ||||||
| 
 | 
 | ||||||
|         when (axis) { |         when (axis) { | ||||||
|             MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> |             MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> | ||||||
| @ -297,7 +309,7 @@ class InputHandler { | |||||||
| 
 | 
 | ||||||
|     private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { |     private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { | ||||||
|         // Joycon support is half dead. Right joystick doesn't work |         // 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) { |         when (axis) { | ||||||
|             MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> |             MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> | ||||||
| @ -325,7 +337,7 @@ class InputHandler { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun setRazerAxisInput(event: MotionEvent, axis: Int) { |     private fun setRazerAxisInput(event: MotionEvent, axis: Int) { | ||||||
|         val playerNumber = getPlayerNumber(event.device.controllerNumber) |         val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) | ||||||
| 
 | 
 | ||||||
|         when (axis) { |         when (axis) { | ||||||
|             MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> |             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