diff options
| author | 2023-04-04 02:08:53 -0600 | |
|---|---|---|
| committer | 2023-06-03 00:05:52 -0700 | |
| commit | 6dfe4240acebe91ee466b970834e71f131d0b349 (patch) | |
| tree | 8bcdb9370b3101c8db286f60e366d665b9d48c1a /src/android | |
| parent | android: Bump minimum version to Android 11 (diff) | |
| download | yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.gz yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.xz yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.zip | |
android: Implement gamepad input
Diffstat (limited to 'src/android')
6 files changed, 510 insertions, 11 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index cd9bc9ef0..c11b6bc16 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | |||
| @@ -43,6 +43,21 @@ object NativeLibrary { | |||
| 43 | const val Player8Device = 7 | 43 | const val Player8Device = 7 |
| 44 | const val ConsoleDevice = 8 | 44 | const val ConsoleDevice = 8 |
| 45 | 45 | ||
| 46 | /** | ||
| 47 | * Controller type for each device | ||
| 48 | */ | ||
| 49 | const val ProController = 3 | ||
| 50 | const val Handheld = 4 | ||
| 51 | const val JoyconDual = 5 | ||
| 52 | const val JoyconLeft = 6 | ||
| 53 | const val JoyconRight = 7 | ||
| 54 | const val GameCube = 8 | ||
| 55 | const val Pokeball = 9 | ||
| 56 | const val NES = 10 | ||
| 57 | const val SNES = 11 | ||
| 58 | const val N64 = 12 | ||
| 59 | const val SegaGenesis = 13 | ||
| 60 | |||
| 46 | @JvmField | 61 | @JvmField |
| 47 | var sEmulationActivity = WeakReference<EmulationActivity?>(null) | 62 | var sEmulationActivity = WeakReference<EmulationActivity?>(null) |
| 48 | 63 | ||
| @@ -71,6 +86,33 @@ object NativeLibrary { | |||
| 71 | } | 86 | } |
| 72 | 87 | ||
| 73 | /** | 88 | /** |
| 89 | * Returns true if pro controller isn't available and handheld is | ||
| 90 | */ | ||
| 91 | external fun isHandheldOnly(): Boolean | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Changes controller type for a specific device. | ||
| 95 | * | ||
| 96 | * @param Device The input descriptor of the gamepad. | ||
| 97 | * @param Type The NpadStyleIndex of the gamepad. | ||
| 98 | */ | ||
| 99 | external fun setDeviceType(Device: Int, Type: Int): Boolean | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Handles event when a gamepad is connected. | ||
| 103 | * | ||
| 104 | * @param Device The input descriptor of the gamepad. | ||
| 105 | */ | ||
| 106 | external fun onGamePadConnectEvent(Device: Int): Boolean | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Handles event when a gamepad is disconnected. | ||
| 110 | * | ||
| 111 | * @param Device The input descriptor of the gamepad. | ||
| 112 | */ | ||
| 113 | external fun onGamePadDisconnectEvent(Device: Int): Boolean | ||
| 114 | |||
| 115 | /** | ||
| 74 | * Handles button press events for a gamepad. | 116 | * Handles button press events for a gamepad. |
| 75 | * | 117 | * |
| 76 | * @param Device The input descriptor of the gamepad. | 118 | * @param Device The input descriptor of the gamepad. |
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 fd174fd2d..4c57de067 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 | |||
| @@ -8,9 +8,9 @@ import android.content.DialogInterface | |||
| 8 | import android.content.Intent | 8 | import android.content.Intent |
| 9 | import android.graphics.Rect | 9 | import android.graphics.Rect |
| 10 | import android.os.Bundle | 10 | import android.os.Bundle |
| 11 | import android.view.* | ||
| 11 | import android.view.KeyEvent | 12 | import android.view.KeyEvent |
| 12 | import android.view.View | 13 | import android.view.MotionEvent |
| 13 | import android.view.WindowManager | ||
| 14 | import android.view.inputmethod.InputMethodManager | 14 | import android.view.inputmethod.InputMethodManager |
| 15 | import androidx.appcompat.app.AppCompatActivity | 15 | import androidx.appcompat.app.AppCompatActivity |
| 16 | import androidx.preference.PreferenceManager | 16 | import androidx.preference.PreferenceManager |
| @@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings | |||
| 23 | import org.yuzu.yuzu_emu.fragments.EmulationFragment | 23 | import org.yuzu.yuzu_emu.fragments.EmulationFragment |
| 24 | import org.yuzu.yuzu_emu.model.Game | 24 | import org.yuzu.yuzu_emu.model.Game |
| 25 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 25 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| 26 | import org.yuzu.yuzu_emu.utils.InputHandler | ||
| 26 | import org.yuzu.yuzu_emu.utils.NfcReader | 27 | import org.yuzu.yuzu_emu.utils.NfcReader |
| 27 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable | 28 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable |
| 28 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 29 | import org.yuzu.yuzu_emu.utils.ThemeHelper |
| @@ -38,6 +39,7 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 38 | private var menuVisible = false | 39 | private var menuVisible = false |
| 39 | private var emulationFragment: EmulationFragment? = null | 40 | private var emulationFragment: EmulationFragment? = null |
| 40 | private lateinit var nfcReader: NfcReader | 41 | private lateinit var nfcReader: NfcReader |
| 42 | private lateinit var inputHandler: InputHandler | ||
| 41 | 43 | ||
| 42 | private lateinit var game: Game | 44 | private lateinit var game: Game |
| 43 | 45 | ||
| @@ -80,6 +82,9 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 80 | nfcReader = NfcReader(this) | 82 | nfcReader = NfcReader(this) |
| 81 | nfcReader.initialize() | 83 | nfcReader.initialize() |
| 82 | 84 | ||
| 85 | inputHandler = InputHandler() | ||
| 86 | inputHandler.initialize() | ||
| 87 | |||
| 83 | // Start a foreground service to prevent the app from getting killed in the background | 88 | // Start a foreground service to prevent the app from getting killed in the background |
| 84 | // TODO(bunnei): Disable notifications until we support app suspension. | 89 | // TODO(bunnei): Disable notifications until we support app suspension. |
| 85 | //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); | 90 | //foregroundService = new Intent(EmulationActivity.this, ForegroundService.class); |
| @@ -108,6 +113,7 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 108 | } | 113 | } |
| 109 | return super.onKeyDown(keyCode, event) | 114 | return super.onKeyDown(keyCode, event) |
| 110 | } | 115 | } |
| 116 | |||
| 111 | override fun onResume() { | 117 | override fun onResume() { |
| 112 | super.onResume() | 118 | super.onResume() |
| 113 | nfcReader.startScanning() | 119 | nfcReader.startScanning() |
| @@ -129,6 +135,29 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 129 | super.onSaveInstanceState(outState) | 135 | super.onSaveInstanceState(outState) |
| 130 | } | 136 | } |
| 131 | 137 | ||
| 138 | override fun dispatchKeyEvent(event: KeyEvent): Boolean { | ||
| 139 | // Handling the case where the back button is pressed. | ||
| 140 | if (event.keyCode == KeyEvent.KEYCODE_BACK) { | ||
| 141 | onBackPressedDispatcher.onBackPressed() | ||
| 142 | return true | ||
| 143 | } | ||
| 144 | |||
| 145 | return inputHandler.dispatchKeyEvent(event) | ||
| 146 | } | ||
| 147 | |||
| 148 | override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { | ||
| 149 | if (event.source and InputDevice.SOURCE_CLASS_JOYSTICK === 0) { | ||
| 150 | return super.dispatchGenericMotionEvent(event) | ||
| 151 | } | ||
| 152 | |||
| 153 | // Don't attempt to do anything if we are disconnecting a device. | ||
| 154 | if (event.actionMasked == MotionEvent.ACTION_CANCEL) { | ||
| 155 | return true | ||
| 156 | } | ||
| 157 | |||
| 158 | return inputHandler.dispatchGenericMotionEvent(event) | ||
| 159 | } | ||
| 160 | |||
| 132 | private fun restoreState(savedInstanceState: Bundle) { | 161 | private fun restoreState(savedInstanceState: Bundle) { |
| 133 | game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! | 162 | game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! |
| 134 | } | 163 | } |
| @@ -159,8 +188,9 @@ open class EmulationActivity : AppCompatActivity() { | |||
| 159 | private fun adjustScale() { | 188 | private fun adjustScale() { |
| 160 | val sliderBinding = DialogSliderBinding.inflate(layoutInflater) | 189 | val sliderBinding = DialogSliderBinding.inflate(layoutInflater) |
| 161 | sliderBinding.slider.valueTo = 150F | 190 | sliderBinding.slider.valueTo = 150F |
| 162 | sliderBinding.slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext) | 191 | sliderBinding.slider.value = |
| 163 | .getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() | 192 | PreferenceManager.getDefaultSharedPreferences(applicationContext) |
| 193 | .getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() | ||
| 164 | sliderBinding.slider.addOnChangeListener(OnChangeListener { _, value, _ -> | 194 | sliderBinding.slider.addOnChangeListener(OnChangeListener { _, value, _ -> |
| 165 | sliderBinding.textValue.text = value.toString() | 195 | sliderBinding.textValue.text = value.toString() |
| 166 | setControlScale(value.toInt()) | 196 | setControlScale(value.toInt()) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index c0dc8ce76..c2adf0ec6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt | |||
| @@ -113,13 +113,15 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context | |||
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | var shouldUpdateView = false | 115 | var shouldUpdateView = false |
| 116 | val playerIndex = | ||
| 117 | if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device | ||
| 116 | 118 | ||
| 117 | for (button in overlayButtons) { | 119 | for (button in overlayButtons) { |
| 118 | if (!button.updateStatus(event)) { | 120 | if (!button.updateStatus(event)) { |
| 119 | continue | 121 | continue |
| 120 | } | 122 | } |
| 121 | NativeLibrary.onGamePadButtonEvent( | 123 | NativeLibrary.onGamePadButtonEvent( |
| 122 | NativeLibrary.Player1Device, | 124 | playerIndex, |
| 123 | button.buttonId, | 125 | button.buttonId, |
| 124 | button.status | 126 | button.status |
| 125 | ) | 127 | ) |
| @@ -131,22 +133,22 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context | |||
| 131 | continue | 133 | continue |
| 132 | } | 134 | } |
| 133 | NativeLibrary.onGamePadButtonEvent( | 135 | NativeLibrary.onGamePadButtonEvent( |
| 134 | NativeLibrary.Player1Device, | 136 | playerIndex, |
| 135 | dpad.upId, | 137 | dpad.upId, |
| 136 | dpad.upStatus | 138 | dpad.upStatus |
| 137 | ) | 139 | ) |
| 138 | NativeLibrary.onGamePadButtonEvent( | 140 | NativeLibrary.onGamePadButtonEvent( |
| 139 | NativeLibrary.Player1Device, | 141 | playerIndex, |
| 140 | dpad.downId, | 142 | dpad.downId, |
| 141 | dpad.downStatus | 143 | dpad.downStatus |
| 142 | ) | 144 | ) |
| 143 | NativeLibrary.onGamePadButtonEvent( | 145 | NativeLibrary.onGamePadButtonEvent( |
| 144 | NativeLibrary.Player1Device, | 146 | playerIndex, |
| 145 | dpad.leftId, | 147 | dpad.leftId, |
| 146 | dpad.leftStatus | 148 | dpad.leftStatus |
| 147 | ) | 149 | ) |
| 148 | NativeLibrary.onGamePadButtonEvent( | 150 | NativeLibrary.onGamePadButtonEvent( |
| 149 | NativeLibrary.Player1Device, | 151 | playerIndex, |
| 150 | dpad.rightId, | 152 | dpad.rightId, |
| 151 | dpad.rightStatus | 153 | dpad.rightStatus |
| 152 | ) | 154 | ) |
| @@ -159,13 +161,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context | |||
| 159 | } | 161 | } |
| 160 | val axisID = joystick.joystickId | 162 | val axisID = joystick.joystickId |
| 161 | NativeLibrary.onGamePadJoystickEvent( | 163 | NativeLibrary.onGamePadJoystickEvent( |
| 162 | NativeLibrary.Player1Device, | 164 | playerIndex, |
| 163 | axisID, | 165 | axisID, |
| 164 | joystick.xAxis, | 166 | joystick.xAxis, |
| 165 | joystick.realYAxis | 167 | joystick.realYAxis |
| 166 | ) | 168 | ) |
| 167 | NativeLibrary.onGamePadButtonEvent( | 169 | NativeLibrary.onGamePadButtonEvent( |
| 168 | NativeLibrary.Player1Device, | 170 | playerIndex, |
| 169 | joystick.buttonId, | 171 | joystick.buttonId, |
| 170 | joystick.buttonStatus | 172 | joystick.buttonStatus |
| 171 | ) | 173 | ) |
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 new file mode 100644 index 000000000..a48b7e00a --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt | |||
| @@ -0,0 +1,323 @@ | |||
| 1 | package org.yuzu.yuzu_emu.utils | ||
| 2 | |||
| 3 | import android.view.InputDevice | ||
| 4 | import android.view.KeyEvent | ||
| 5 | import android.view.MotionEvent | ||
| 6 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 7 | |||
| 8 | class InputHandler { | ||
| 9 | fun initialize() { | ||
| 10 | // Connect first controller | ||
| 11 | NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)); | ||
| 12 | } | ||
| 13 | |||
| 14 | fun dispatchKeyEvent(event: KeyEvent): Boolean { | ||
| 15 | val button: Int = when (event.device.vendorId) { | ||
| 16 | 0x045E -> getInputXboxButtonKey(event.keyCode) | ||
| 17 | 0x054C -> getInputDS5ButtonKey(event.keyCode) | ||
| 18 | 0x057E -> getInputJoyconButtonKey(event.keyCode) | ||
| 19 | 0x1532 -> getInputRazerButtonKey(event.keyCode) | ||
| 20 | else -> getInputGenericButtonKey(event.keyCode) | ||
| 21 | } | ||
| 22 | |||
| 23 | val action = when (event.action) { | ||
| 24 | KeyEvent.ACTION_DOWN -> NativeLibrary.ButtonState.PRESSED | ||
| 25 | KeyEvent.ACTION_UP -> NativeLibrary.ButtonState.RELEASED | ||
| 26 | else -> return false | ||
| 27 | } | ||
| 28 | |||
| 29 | // Ignore invalid buttons | ||
| 30 | if (button < 0) { | ||
| 31 | return false | ||
| 32 | } | ||
| 33 | |||
| 34 | return NativeLibrary.onGamePadButtonEvent( | ||
| 35 | getPlayerNumber(event.device.controllerNumber), | ||
| 36 | button, | ||
| 37 | action | ||
| 38 | ) | ||
| 39 | } | ||
| 40 | |||
| 41 | fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { | ||
| 42 | val device = event.device | ||
| 43 | // Check every axis input available on the controller | ||
| 44 | for (range in device.motionRanges) { | ||
| 45 | val axis = range.axis; | ||
| 46 | when (device.vendorId) { | ||
| 47 | 0x045E -> setGenericAxisInput(event, axis) | ||
| 48 | 0x054C -> setGenericAxisInput(event, axis) | ||
| 49 | 0x057E -> setJoyconAxisInput(event, axis) | ||
| 50 | 0x1532 -> setRazerAxisInput(event, axis) | ||
| 51 | else -> setGenericAxisInput(event, axis) | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | return true | ||
| 56 | } | ||
| 57 | |||
| 58 | private fun getPlayerNumber(index: Int): Int { | ||
| 59 | // TODO: Joycons are handled as different controllers. Find a way to merge them. | ||
| 60 | return when (index) { | ||
| 61 | 2 -> NativeLibrary.Player2Device | ||
| 62 | 3 -> NativeLibrary.Player3Device | ||
| 63 | 4 -> NativeLibrary.Player4Device | ||
| 64 | 5 -> NativeLibrary.Player5Device | ||
| 65 | 6 -> NativeLibrary.Player6Device | ||
| 66 | 7 -> NativeLibrary.Player7Device | ||
| 67 | 8 -> NativeLibrary.Player8Device | ||
| 68 | else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | private fun getAxisToButton(axis: Float): Int { | ||
| 73 | return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED | ||
| 74 | } | ||
| 75 | |||
| 76 | private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) { | ||
| 77 | NativeLibrary.onGamePadButtonEvent( | ||
| 78 | playerNumber, | ||
| 79 | NativeLibrary.ButtonType.DPAD_UP, | ||
| 80 | getAxisToButton(-yAxis) | ||
| 81 | ) | ||
| 82 | NativeLibrary.onGamePadButtonEvent( | ||
| 83 | playerNumber, | ||
| 84 | NativeLibrary.ButtonType.DPAD_DOWN, | ||
| 85 | getAxisToButton(yAxis) | ||
| 86 | ) | ||
| 87 | NativeLibrary.onGamePadButtonEvent( | ||
| 88 | playerNumber, | ||
| 89 | NativeLibrary.ButtonType.DPAD_LEFT, | ||
| 90 | getAxisToButton(-xAxis) | ||
| 91 | ) | ||
| 92 | NativeLibrary.onGamePadButtonEvent( | ||
| 93 | playerNumber, | ||
| 94 | NativeLibrary.ButtonType.DPAD_RIGHT, | ||
| 95 | getAxisToButton(xAxis) | ||
| 96 | ) | ||
| 97 | } | ||
| 98 | |||
| 99 | private fun getInputDS5ButtonKey(key: Int): Int { | ||
| 100 | // The missing ds5 buttons are axis | ||
| 101 | return when (key) { | ||
| 102 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B | ||
| 103 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A | ||
| 104 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 105 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X | ||
| 106 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 107 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 108 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 109 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 110 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 111 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 112 | else -> -1 | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | private fun getInputJoyconButtonKey(key: Int): Int { | ||
| 117 | // Joycon support is half dead. A lot of buttons can't be mapped | ||
| 118 | return when (key) { | ||
| 119 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B | ||
| 120 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A | ||
| 121 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X | ||
| 122 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 123 | KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP | ||
| 124 | KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN | ||
| 125 | KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT | ||
| 126 | KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT | ||
| 127 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 128 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 129 | KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL | ||
| 130 | KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR | ||
| 131 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 132 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 133 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 134 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 135 | else -> -1 | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | private fun getInputXboxButtonKey(key: Int): Int { | ||
| 140 | // The missing xbox buttons are axis | ||
| 141 | return when (key) { | ||
| 142 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A | ||
| 143 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B | ||
| 144 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X | ||
| 145 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 146 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 147 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 148 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 149 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 150 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 151 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 152 | else -> -1 | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | private fun getInputRazerButtonKey(key: Int): Int { | ||
| 157 | // The missing xbox buttons are axis | ||
| 158 | return when (key) { | ||
| 159 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B | ||
| 160 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A | ||
| 161 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 162 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X | ||
| 163 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 164 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 165 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 166 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 167 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 168 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 169 | else -> -1 | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | private fun getInputGenericButtonKey(key: Int): Int { | ||
| 174 | return when (key) { | ||
| 175 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A | ||
| 176 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B | ||
| 177 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X | ||
| 178 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 179 | KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP | ||
| 180 | KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN | ||
| 181 | KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT | ||
| 182 | KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT | ||
| 183 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 184 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 185 | KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL | ||
| 186 | KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR | ||
| 187 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 188 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 189 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 190 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 191 | else -> -1 | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | private fun setGenericAxisInput(event: MotionEvent, axis: Int) { | ||
| 196 | val playerNumber = getPlayerNumber(event.device.controllerNumber) | ||
| 197 | |||
| 198 | when (axis) { | ||
| 199 | MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> | ||
| 200 | NativeLibrary.onGamePadJoystickEvent( | ||
| 201 | playerNumber, | ||
| 202 | NativeLibrary.StickType.STICK_L, | ||
| 203 | event.getAxisValue(MotionEvent.AXIS_X), | ||
| 204 | -event.getAxisValue(MotionEvent.AXIS_Y) | ||
| 205 | ) | ||
| 206 | MotionEvent.AXIS_RX, MotionEvent.AXIS_RY -> | ||
| 207 | NativeLibrary.onGamePadJoystickEvent( | ||
| 208 | playerNumber, | ||
| 209 | NativeLibrary.StickType.STICK_R, | ||
| 210 | event.getAxisValue(MotionEvent.AXIS_RX), | ||
| 211 | -event.getAxisValue(MotionEvent.AXIS_RY) | ||
| 212 | ) | ||
| 213 | MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ -> | ||
| 214 | NativeLibrary.onGamePadJoystickEvent( | ||
| 215 | playerNumber, | ||
| 216 | NativeLibrary.StickType.STICK_R, | ||
| 217 | event.getAxisValue(MotionEvent.AXIS_Z), | ||
| 218 | -event.getAxisValue(MotionEvent.AXIS_RZ) | ||
| 219 | ) | ||
| 220 | MotionEvent.AXIS_LTRIGGER -> | ||
| 221 | NativeLibrary.onGamePadButtonEvent( | ||
| 222 | playerNumber, | ||
| 223 | NativeLibrary.ButtonType.TRIGGER_ZL, | ||
| 224 | getAxisToButton(event.getAxisValue(MotionEvent.AXIS_LTRIGGER)) | ||
| 225 | ) | ||
| 226 | MotionEvent.AXIS_BRAKE -> | ||
| 227 | NativeLibrary.onGamePadButtonEvent( | ||
| 228 | playerNumber, | ||
| 229 | NativeLibrary.ButtonType.TRIGGER_ZL, | ||
| 230 | getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE)) | ||
| 231 | ) | ||
| 232 | MotionEvent.AXIS_RTRIGGER -> | ||
| 233 | NativeLibrary.onGamePadButtonEvent( | ||
| 234 | playerNumber, | ||
| 235 | NativeLibrary.ButtonType.TRIGGER_ZR, | ||
| 236 | getAxisToButton(event.getAxisValue(MotionEvent.AXIS_RTRIGGER)) | ||
| 237 | ) | ||
| 238 | MotionEvent.AXIS_GAS -> | ||
| 239 | NativeLibrary.onGamePadButtonEvent( | ||
| 240 | playerNumber, | ||
| 241 | NativeLibrary.ButtonType.TRIGGER_ZR, | ||
| 242 | getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS)) | ||
| 243 | ) | ||
| 244 | MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y -> | ||
| 245 | setAxisDpadState( | ||
| 246 | playerNumber, | ||
| 247 | event.getAxisValue(MotionEvent.AXIS_HAT_X), | ||
| 248 | event.getAxisValue(MotionEvent.AXIS_HAT_Y) | ||
| 249 | ) | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | |||
| 254 | private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { | ||
| 255 | // Joycon support is half dead. Right joystick doesn't work | ||
| 256 | val playerNumber = getPlayerNumber(event.device.controllerNumber) | ||
| 257 | |||
| 258 | when (axis) { | ||
| 259 | MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> | ||
| 260 | NativeLibrary.onGamePadJoystickEvent( | ||
| 261 | playerNumber, | ||
| 262 | NativeLibrary.StickType.STICK_L, | ||
| 263 | event.getAxisValue(MotionEvent.AXIS_X), | ||
| 264 | -event.getAxisValue(MotionEvent.AXIS_Y) | ||
| 265 | ) | ||
| 266 | MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ -> | ||
| 267 | NativeLibrary.onGamePadJoystickEvent( | ||
| 268 | playerNumber, | ||
| 269 | NativeLibrary.StickType.STICK_R, | ||
| 270 | event.getAxisValue(MotionEvent.AXIS_Z), | ||
| 271 | -event.getAxisValue(MotionEvent.AXIS_RZ) | ||
| 272 | ) | ||
| 273 | MotionEvent.AXIS_RX, MotionEvent.AXIS_RY -> | ||
| 274 | NativeLibrary.onGamePadJoystickEvent( | ||
| 275 | playerNumber, | ||
| 276 | NativeLibrary.StickType.STICK_R, | ||
| 277 | event.getAxisValue(MotionEvent.AXIS_RX), | ||
| 278 | -event.getAxisValue(MotionEvent.AXIS_RY) | ||
| 279 | ) | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | private fun setRazerAxisInput(event: MotionEvent, axis: Int) { | ||
| 284 | val playerNumber = getPlayerNumber(event.device.controllerNumber) | ||
| 285 | |||
| 286 | when (axis) { | ||
| 287 | MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> | ||
| 288 | NativeLibrary.onGamePadJoystickEvent( | ||
| 289 | playerNumber, | ||
| 290 | NativeLibrary.StickType.STICK_L, | ||
| 291 | event.getAxisValue(MotionEvent.AXIS_X), | ||
| 292 | -event.getAxisValue(MotionEvent.AXIS_Y) | ||
| 293 | ) | ||
| 294 | MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ -> | ||
| 295 | NativeLibrary.onGamePadJoystickEvent( | ||
| 296 | playerNumber, | ||
| 297 | NativeLibrary.StickType.STICK_R, | ||
| 298 | event.getAxisValue(MotionEvent.AXIS_Z), | ||
| 299 | -event.getAxisValue(MotionEvent.AXIS_RZ) | ||
| 300 | ) | ||
| 301 | MotionEvent.AXIS_BRAKE -> | ||
| 302 | NativeLibrary.onGamePadButtonEvent( | ||
| 303 | playerNumber, | ||
| 304 | NativeLibrary.ButtonType.TRIGGER_ZL, | ||
| 305 | getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE)) | ||
| 306 | ) | ||
| 307 | MotionEvent.AXIS_GAS -> | ||
| 308 | NativeLibrary.onGamePadButtonEvent( | ||
| 309 | playerNumber, | ||
| 310 | NativeLibrary.ButtonType.TRIGGER_ZR, | ||
| 311 | getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS)) | ||
| 312 | ) | ||
| 313 | MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y -> | ||
| 314 | setAxisDpadState( | ||
| 315 | playerNumber, | ||
| 316 | event.getAxisValue(MotionEvent.AXIS_HAT_X), | ||
| 317 | event.getAxisValue(MotionEvent.AXIS_HAT_Y) | ||
| 318 | ) | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | |||
| 323 | } \ No newline at end of file | ||
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index b10c55a45..a7321af2a 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -38,6 +38,8 @@ | |||
| 38 | #include "core/frontend/applets/software_keyboard.h" | 38 | #include "core/frontend/applets/software_keyboard.h" |
| 39 | #include "core/frontend/applets/web_browser.h" | 39 | #include "core/frontend/applets/web_browser.h" |
| 40 | #include "core/hid/hid_core.h" | 40 | #include "core/hid/hid_core.h" |
| 41 | #include "core/hid/emulated_controller.h" | ||
| 42 | #include "core/hid/hid_types.h" | ||
| 41 | #include "core/hle/service/acc/profile_manager.h" | 43 | #include "core/hle/service/acc/profile_manager.h" |
| 42 | #include "core/hle/service/am/applet_ae.h" | 44 | #include "core/hle/service/am/applet_ae.h" |
| 43 | #include "core/hle/service/am/applet_oe.h" | 45 | #include "core/hle/service/am/applet_oe.h" |
| @@ -274,6 +276,60 @@ public: | |||
| 274 | m_rom_metadata_cache.clear(); | 276 | m_rom_metadata_cache.clear(); |
| 275 | } | 277 | } |
| 276 | 278 | ||
| 279 | bool IsHandheldOnly(){ | ||
| 280 | const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); | ||
| 281 | |||
| 282 | if (npad_style_set.fullkey == 1) { | ||
| 283 | return false; | ||
| 284 | } | ||
| 285 | |||
| 286 | if (npad_style_set.handheld == 0) { | ||
| 287 | return false; | ||
| 288 | } | ||
| 289 | |||
| 290 | return !Settings::values.use_docked_mode.GetValue(); | ||
| 291 | } | ||
| 292 | |||
| 293 | void SetDeviceType(int index, int type){ | ||
| 294 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | ||
| 295 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); | ||
| 296 | } | ||
| 297 | |||
| 298 | void OnGamepadConnectEvent(int index){ | ||
| 299 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | ||
| 300 | |||
| 301 | // Ensure that player1 is configured correctly and handheld disconnected | ||
| 302 | if(controller->GetNpadIdType() == Core::HID::NpadIdType::Player1){ | ||
| 303 | auto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | ||
| 304 | |||
| 305 | if(controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { | ||
| 306 | handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | ||
| 307 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); | ||
| 308 | handheld->Disconnect(); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | // Ensure that handheld is configured correctly and player 1 disconnected | ||
| 313 | if(controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld){ | ||
| 314 | auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 315 | |||
| 316 | if(controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { | ||
| 317 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | ||
| 318 | controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | ||
| 319 | player1->Disconnect(); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | if(!controller->IsConnected()){ | ||
| 324 | controller->Connect(); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | void OnGamepadDisconnectEvent(int index){ | ||
| 329 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | ||
| 330 | controller->Disconnect(); | ||
| 331 | } | ||
| 332 | |||
| 277 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { | 333 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() { |
| 278 | return m_software_keyboard; | 334 | return m_software_keyboard; |
| 279 | } | 335 | } |
| @@ -440,11 +496,45 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv | |||
| 440 | return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); | 496 | return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning()); |
| 441 | } | 497 | } |
| 442 | 498 | ||
| 499 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env, | ||
| 500 | [[maybe_unused]] jclass clazz) { | ||
| 501 | return EmulationSession::GetInstance().IsHandheldOnly(); | ||
| 502 | } | ||
| 503 | |||
| 504 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env, | ||
| 505 | [[maybe_unused]] jclass clazz, | ||
| 506 | jint j_device, | ||
| 507 | jint j_type) { | ||
| 508 | if (EmulationSession::GetInstance().IsRunning()) { | ||
| 509 | EmulationSession::GetInstance().SetDeviceType(j_device, j_type); | ||
| 510 | } | ||
| 511 | return static_cast<jboolean>(true); | ||
| 512 | } | ||
| 513 | |||
| 514 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env, | ||
| 515 | [[maybe_unused]] jclass clazz, | ||
| 516 | jint j_device) { | ||
| 517 | if (EmulationSession::GetInstance().IsRunning()) { | ||
| 518 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); | ||
| 519 | } | ||
| 520 | return static_cast<jboolean>(true); | ||
| 521 | } | ||
| 522 | |||
| 523 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent([[maybe_unused]] JNIEnv* env, | ||
| 524 | [[maybe_unused]] jclass clazz, | ||
| 525 | jint j_device) { | ||
| 526 | if (EmulationSession::GetInstance().IsRunning()) { | ||
| 527 | EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); | ||
| 528 | } | ||
| 529 | return static_cast<jboolean>(true); | ||
| 530 | } | ||
| 443 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, | 531 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env, |
| 444 | [[maybe_unused]] jclass clazz, | 532 | [[maybe_unused]] jclass clazz, |
| 445 | [[maybe_unused]] jint j_device, | 533 | [[maybe_unused]] jint j_device, |
| 446 | jint j_button, jint action) { | 534 | jint j_button, jint action) { |
| 447 | if (EmulationSession::GetInstance().IsRunning()) { | 535 | if (EmulationSession::GetInstance().IsRunning()) { |
| 536 | // Ensure gamepad is connected | ||
| 537 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); | ||
| 448 | EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button, | 538 | EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button, |
| 449 | action != 0); | 539 | action != 0); |
| 450 | } | 540 | } |
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 8336e525a..d1e382a33 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h | |||
| @@ -25,6 +25,18 @@ JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JN | |||
| 25 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env, | 25 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env, |
| 26 | jclass clazz); | 26 | jclass clazz); |
| 27 | 27 | ||
| 28 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly( | ||
| 29 | JNIEnv* env, jclass clazz); | ||
| 30 | |||
| 31 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType( | ||
| 32 | JNIEnv* env, jclass clazz, jstring j_device, jstring j_type); | ||
| 33 | |||
| 34 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent( | ||
| 35 | JNIEnv* env, jclass clazz, jstring j_device); | ||
| 36 | |||
| 37 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent( | ||
| 38 | JNIEnv* env, jclass clazz, jstring j_device); | ||
| 39 | |||
| 28 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent( | 40 | JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent( |
| 29 | JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action); | 41 | JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action); |
| 30 | 42 | ||