From 9b3c64f4a40872208bfd0e8c90174d724ee5e9e6 Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Mon, 30 Oct 2023 00:32:43 -0400
Subject: [PATCH 1/3] android: Removed unused ControllerMappingHelper

---
 .../yuzu_emu/activities/EmulationActivity.kt  |  5 --
 .../yuzu_emu/utils/ControllerMappingHelper.kt | 70 -------------------
 2 files changed, 75 deletions(-)
 delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt

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 e96a2059b1..7464647c4e 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,8 +56,6 @@ 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
@@ -95,8 +92,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()
 
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 eeefcdf205..0000000000
--- 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
-    }
-}

From 70be45c992218469f6884516f9f22e373cd0c3b1 Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Mon, 30 Oct 2023 01:09:14 -0400
Subject: [PATCH 2/3] android: InputHandler: Convert to object

This doesn't need to be an instance of a class because it doesn't hold any data. It's just all helper functions.
---
 .../org/yuzu/yuzu_emu/activities/EmulationActivity.kt     | 8 +++-----
 .../src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt | 2 +-
 2 files changed, 4 insertions(+), 6 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 7464647c4e..0eda27f7d9 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
@@ -58,7 +58,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 
     var isActivityRecreated = false
     private lateinit var nfcReader: NfcReader
-    private lateinit var inputHandler: InputHandler
 
     private val gyro = FloatArray(3)
     private val accel = FloatArray(3)
@@ -100,8 +99,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)) {
@@ -190,7 +188,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
             return super.dispatchKeyEvent(event)
         }
 
-        return inputHandler.dispatchKeyEvent(event)
+        return InputHandler.dispatchKeyEvent(event)
     }
 
     override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
@@ -205,7 +203,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 e963dfbc17..fec40e27d8 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
@@ -8,7 +8,7 @@ import android.view.MotionEvent
 import kotlin.math.sqrt
 import org.yuzu.yuzu_emu.NativeLibrary
 
-class InputHandler {
+object InputHandler {
     fun initialize() {
         // Connect first controller
         NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device))

From f7755df2af7942ddcec09110ff7489cd3792fbda Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Mon, 30 Oct 2023 11:27:19 -0400
Subject: [PATCH 3/3] android: Reorder controller indexes and only use
 controllers

Before we could ignore controller inputs by forwarding them to player two if a non-controller was connected before and recognized as an input device.
---
 .../yuzu_emu/activities/EmulationActivity.kt  |  3 ++
 .../org/yuzu/yuzu_emu/utils/InputHandler.kt   | 53 ++++++++++++++++---
 2 files changed, 50 insertions(+), 6 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 0eda27f7d9..f37875ffea 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
@@ -64,6 +64,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
     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"
@@ -155,6 +157,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
         super.onResume()
         nfcReader.startScanning()
         startMotionSensorListener()
+        InputHandler.updateControllerIds()
 
         buildPictureInPictureParams()
     }
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 fec40e27d8..fc6a8b5cb7 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
 
 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 @@ object InputHandler {
         }
 
         return NativeLibrary.onGamePadButtonEvent(
-            getPlayerNumber(event.device.controllerNumber),
+            getPlayerNumber(event.device.controllerNumber, event.deviceId),
             button,
             action
         )
@@ -58,9 +65,14 @@ object 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 @@ object 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 @@ object 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 @@ object 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 @@ object 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
+    }
 }