summaryrefslogtreecommitdiffstats
path: root/src/android/app
diff options
context:
space:
mode:
authorgerman77 <juangerman-13@hotmail.com>2023-04-04 10:08:53 +0200
committerbunnei <bunneidev@gmail.com>2023-06-03 09:05:52 +0200
commit6dfe4240acebe91ee466b970834e71f131d0b349 (patch)
tree8bcdb9370b3101c8db286f60e366d665b9d48c1a /src/android/app
parentandroid: Bump minimum version to Android 11 (diff)
downloadyuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar
yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.gz
yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.bz2
yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.lz
yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.xz
yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.tar.zst
yuzu-6dfe4240acebe91ee466b970834e71f131d0b349.zip
Diffstat (limited to 'src/android/app')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt323
-rw-r--r--src/android/app/src/main/jni/native.cpp90
-rw-r--r--src/android/app/src/main/jni/native.h12
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 {
const val Player8Device = 7
const val ConsoleDevice = 8
+ /**
+ * Controller type for each device
+ */
+ const val ProController = 3
+ const val Handheld = 4
+ const val JoyconDual = 5
+ const val JoyconLeft = 6
+ const val JoyconRight = 7
+ const val GameCube = 8
+ const val Pokeball = 9
+ const val NES = 10
+ const val SNES = 11
+ const val N64 = 12
+ const val SegaGenesis = 13
+
@JvmField
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
@@ -71,6 +86,33 @@ object NativeLibrary {
}
/**
+ * Returns true if pro controller isn't available and handheld is
+ */
+ external fun isHandheldOnly(): Boolean
+
+ /**
+ * Changes controller type for a specific device.
+ *
+ * @param Device The input descriptor of the gamepad.
+ * @param Type The NpadStyleIndex of the gamepad.
+ */
+ external fun setDeviceType(Device: Int, Type: Int): Boolean
+
+ /**
+ * Handles event when a gamepad is connected.
+ *
+ * @param Device The input descriptor of the gamepad.
+ */
+ external fun onGamePadConnectEvent(Device: Int): Boolean
+
+ /**
+ * Handles event when a gamepad is disconnected.
+ *
+ * @param Device The input descriptor of the gamepad.
+ */
+ external fun onGamePadDisconnectEvent(Device: Int): Boolean
+
+ /**
* Handles button press events for a gamepad.
*
* @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
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
+import android.view.*
import android.view.KeyEvent
-import android.view.View
-import android.view.WindowManager
+import android.view.MotionEvent
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
@@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
+import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
import org.yuzu.yuzu_emu.utils.ThemeHelper
@@ -38,6 +39,7 @@ open class EmulationActivity : AppCompatActivity() {
private var menuVisible = false
private var emulationFragment: EmulationFragment? = null
private lateinit var nfcReader: NfcReader
+ private lateinit var inputHandler: InputHandler
private lateinit var game: Game
@@ -80,6 +82,9 @@ open class EmulationActivity : AppCompatActivity() {
nfcReader = NfcReader(this)
nfcReader.initialize()
+ inputHandler = InputHandler()
+ inputHandler.initialize()
+
// Start a foreground service to prevent the app from getting killed in the background
// TODO(bunnei): Disable notifications until we support app suspension.
//foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
@@ -108,6 +113,7 @@ open class EmulationActivity : AppCompatActivity() {
}
return super.onKeyDown(keyCode, event)
}
+
override fun onResume() {
super.onResume()
nfcReader.startScanning()
@@ -129,6 +135,29 @@ open class EmulationActivity : AppCompatActivity() {
super.onSaveInstanceState(outState)
}
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ // Handling the case where the back button is pressed.
+ if (event.keyCode == KeyEvent.KEYCODE_BACK) {
+ onBackPressedDispatcher.onBackPressed()
+ return true
+ }
+
+ return inputHandler.dispatchKeyEvent(event)
+ }
+
+ override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
+ if (event.source and InputDevice.SOURCE_CLASS_JOYSTICK === 0) {
+ return super.dispatchGenericMotionEvent(event)
+ }
+
+ // Don't attempt to do anything if we are disconnecting a device.
+ if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
+ return true
+ }
+
+ return inputHandler.dispatchGenericMotionEvent(event)
+ }
+
private fun restoreState(savedInstanceState: Bundle) {
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
}
@@ -159,8 +188,9 @@ open class EmulationActivity : AppCompatActivity() {
private fun adjustScale() {
val sliderBinding = DialogSliderBinding.inflate(layoutInflater)
sliderBinding.slider.valueTo = 150F
- sliderBinding.slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext)
- .getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
+ sliderBinding.slider.value =
+ PreferenceManager.getDefaultSharedPreferences(applicationContext)
+ .getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
sliderBinding.slider.addOnChangeListener(OnChangeListener { _, value, _ ->
sliderBinding.textValue.text = value.toString()
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
}
var shouldUpdateView = false
+ val playerIndex =
+ if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
continue
}
NativeLibrary.onGamePadButtonEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
button.buttonId,
button.status
)
@@ -131,22 +133,22 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
continue
}
NativeLibrary.onGamePadButtonEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
dpad.upId,
dpad.upStatus
)
NativeLibrary.onGamePadButtonEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
dpad.downId,
dpad.downStatus
)
NativeLibrary.onGamePadButtonEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
dpad.leftId,
dpad.leftStatus
)
NativeLibrary.onGamePadButtonEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
dpad.rightId,
dpad.rightStatus
)
@@ -159,13 +161,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
}
val axisID = joystick.joystickId
NativeLibrary.onGamePadJoystickEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
axisID,
joystick.xAxis,
joystick.realYAxis
)
NativeLibrary.onGamePadButtonEvent(
- NativeLibrary.Player1Device,
+ playerIndex,
joystick.buttonId,
joystick.buttonStatus
)
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 @@
+package org.yuzu.yuzu_emu.utils
+
+import android.view.InputDevice
+import android.view.KeyEvent
+import android.view.MotionEvent
+import org.yuzu.yuzu_emu.NativeLibrary
+
+class InputHandler {
+ fun initialize() {
+ // Connect first controller
+ NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device));
+ }
+
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ val button: Int = when (event.device.vendorId) {
+ 0x045E -> getInputXboxButtonKey(event.keyCode)
+ 0x054C -> getInputDS5ButtonKey(event.keyCode)
+ 0x057E -> getInputJoyconButtonKey(event.keyCode)
+ 0x1532 -> getInputRazerButtonKey(event.keyCode)
+ else -> getInputGenericButtonKey(event.keyCode)
+ }
+
+ val action = when (event.action) {
+ KeyEvent.ACTION_DOWN -> NativeLibrary.ButtonState.PRESSED
+ KeyEvent.ACTION_UP -> NativeLibrary.ButtonState.RELEASED
+ else -> return false
+ }
+
+ // Ignore invalid buttons
+ if (button < 0) {
+ return false
+ }
+
+ return NativeLibrary.onGamePadButtonEvent(
+ getPlayerNumber(event.device.controllerNumber),
+ button,
+ action
+ )
+ }
+
+ fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
+ val device = event.device
+ // Check every axis input available on the controller
+ for (range in device.motionRanges) {
+ val axis = range.axis;
+ when (device.vendorId) {
+ 0x045E -> setGenericAxisInput(event, axis)
+ 0x054C -> setGenericAxisInput(event, axis)
+ 0x057E -> setJoyconAxisInput(event, axis)
+ 0x1532 -> setRazerAxisInput(event, axis)
+ else -> setGenericAxisInput(event, axis)
+ }
+ }
+
+ return true
+ }
+
+ private fun getPlayerNumber(index: Int): Int {
+ // TODO: Joycons are handled as different controllers. Find a way to merge them.
+ return when (index) {
+ 2 -> NativeLibrary.Player2Device
+ 3 -> NativeLibrary.Player3Device
+ 4 -> NativeLibrary.Player4Device
+ 5 -> NativeLibrary.Player5Device
+ 6 -> NativeLibrary.Player6Device
+ 7 -> NativeLibrary.Player7Device
+ 8 -> NativeLibrary.Player8Device
+ else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
+ }
+ }
+
+ private fun getAxisToButton(axis: Float): Int {
+ return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED
+ }
+
+ private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.DPAD_UP,
+ getAxisToButton(-yAxis)
+ )
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.DPAD_DOWN,
+ getAxisToButton(yAxis)
+ )
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.DPAD_LEFT,
+ getAxisToButton(-xAxis)
+ )
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.DPAD_RIGHT,
+ getAxisToButton(xAxis)
+ )
+ }
+
+ private fun getInputDS5ButtonKey(key: Int): Int {
+ // The missing ds5 buttons are axis
+ return when (key) {
+ KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
+ KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
+ KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
+ KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
+ KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
+ KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
+ KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
+ KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
+ KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
+ else -> -1
+ }
+ }
+
+ private fun getInputJoyconButtonKey(key: Int): Int {
+ // Joycon support is half dead. A lot of buttons can't be mapped
+ return when (key) {
+ KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
+ KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
+ KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
+ KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
+ KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP
+ KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
+ KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
+ KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
+ KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
+ KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
+ KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
+ KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
+ KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
+ KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
+ KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
+ else -> -1
+ }
+ }
+
+ private fun getInputXboxButtonKey(key: Int): Int {
+ // The missing xbox buttons are axis
+ return when (key) {
+ KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
+ KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B
+ KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
+ KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
+ KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
+ KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
+ KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
+ KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
+ KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
+ else -> -1
+ }
+ }
+
+ private fun getInputRazerButtonKey(key: Int): Int {
+ // The missing xbox buttons are axis
+ return when (key) {
+ KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
+ KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
+ KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
+ KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
+ KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
+ KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
+ KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
+ KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
+ KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
+ else -> -1
+ }
+ }
+
+ private fun getInputGenericButtonKey(key: Int): Int {
+ return when (key) {
+ KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
+ KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B
+ KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
+ KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
+ KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP
+ KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
+ KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
+ KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
+ KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
+ KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
+ KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
+ KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
+ KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
+ KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
+ KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
+ else -> -1
+ }
+ }
+
+ private fun setGenericAxisInput(event: MotionEvent, axis: Int) {
+ val playerNumber = getPlayerNumber(event.device.controllerNumber)
+
+ when (axis) {
+ MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_L,
+ event.getAxisValue(MotionEvent.AXIS_X),
+ -event.getAxisValue(MotionEvent.AXIS_Y)
+ )
+ MotionEvent.AXIS_RX, MotionEvent.AXIS_RY ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_R,
+ event.getAxisValue(MotionEvent.AXIS_RX),
+ -event.getAxisValue(MotionEvent.AXIS_RY)
+ )
+ MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_R,
+ event.getAxisValue(MotionEvent.AXIS_Z),
+ -event.getAxisValue(MotionEvent.AXIS_RZ)
+ )
+ MotionEvent.AXIS_LTRIGGER ->
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.TRIGGER_ZL,
+ getAxisToButton(event.getAxisValue(MotionEvent.AXIS_LTRIGGER))
+ )
+ MotionEvent.AXIS_BRAKE ->
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.TRIGGER_ZL,
+ getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE))
+ )
+ MotionEvent.AXIS_RTRIGGER ->
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.TRIGGER_ZR,
+ getAxisToButton(event.getAxisValue(MotionEvent.AXIS_RTRIGGER))
+ )
+ MotionEvent.AXIS_GAS ->
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.TRIGGER_ZR,
+ getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS))
+ )
+ MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y ->
+ setAxisDpadState(
+ playerNumber,
+ event.getAxisValue(MotionEvent.AXIS_HAT_X),
+ event.getAxisValue(MotionEvent.AXIS_HAT_Y)
+ )
+ }
+ }
+
+
+ private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
+ // Joycon support is half dead. Right joystick doesn't work
+ val playerNumber = getPlayerNumber(event.device.controllerNumber)
+
+ when (axis) {
+ MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_L,
+ event.getAxisValue(MotionEvent.AXIS_X),
+ -event.getAxisValue(MotionEvent.AXIS_Y)
+ )
+ MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_R,
+ event.getAxisValue(MotionEvent.AXIS_Z),
+ -event.getAxisValue(MotionEvent.AXIS_RZ)
+ )
+ MotionEvent.AXIS_RX, MotionEvent.AXIS_RY ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_R,
+ event.getAxisValue(MotionEvent.AXIS_RX),
+ -event.getAxisValue(MotionEvent.AXIS_RY)
+ )
+ }
+ }
+
+ private fun setRazerAxisInput(event: MotionEvent, axis: Int) {
+ val playerNumber = getPlayerNumber(event.device.controllerNumber)
+
+ when (axis) {
+ MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_L,
+ event.getAxisValue(MotionEvent.AXIS_X),
+ -event.getAxisValue(MotionEvent.AXIS_Y)
+ )
+ MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
+ NativeLibrary.onGamePadJoystickEvent(
+ playerNumber,
+ NativeLibrary.StickType.STICK_R,
+ event.getAxisValue(MotionEvent.AXIS_Z),
+ -event.getAxisValue(MotionEvent.AXIS_RZ)
+ )
+ MotionEvent.AXIS_BRAKE ->
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.TRIGGER_ZL,
+ getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE))
+ )
+ MotionEvent.AXIS_GAS ->
+ NativeLibrary.onGamePadButtonEvent(
+ playerNumber,
+ NativeLibrary.ButtonType.TRIGGER_ZR,
+ getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS))
+ )
+ MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y ->
+ setAxisDpadState(
+ playerNumber,
+ event.getAxisValue(MotionEvent.AXIS_HAT_X),
+ event.getAxisValue(MotionEvent.AXIS_HAT_Y)
+ )
+ }
+ }
+
+
+} \ 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 @@
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hid/hid_core.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_types.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@@ -274,6 +276,60 @@ public:
m_rom_metadata_cache.clear();
}
+ bool IsHandheldOnly(){
+ const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
+
+ if (npad_style_set.fullkey == 1) {
+ return false;
+ }
+
+ if (npad_style_set.handheld == 0) {
+ return false;
+ }
+
+ return !Settings::values.use_docked_mode.GetValue();
+ }
+
+ void SetDeviceType(int index, int type){
+ auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
+ controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
+ }
+
+ void OnGamepadConnectEvent(int index){
+ auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
+
+ // Ensure that player1 is configured correctly and handheld disconnected
+ if(controller->GetNpadIdType() == Core::HID::NpadIdType::Player1){
+ auto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+
+ if(controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
+ handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
+ handheld->Disconnect();
+ }
+ }
+
+ // Ensure that handheld is configured correctly and player 1 disconnected
+ if(controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld){
+ auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
+
+ if(controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
+ player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
+ player1->Disconnect();
+ }
+ }
+
+ if(!controller->IsConnected()){
+ controller->Connect();
+ }
+ }
+
+ void OnGamepadDisconnectEvent(int index){
+ auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
+ controller->Disconnect();
+ }
+
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() {
return m_software_keyboard;
}
@@ -440,11 +496,45 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz) {
+ return EmulationSession::GetInstance().IsHandheldOnly();
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz,
+ jint j_device,
+ jint j_type) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
+ }
+ return static_cast<jboolean>(true);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz,
+ jint j_device) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
+ }
+ return static_cast<jboolean>(true);
+}
+
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jclass clazz,
+ jint j_device) {
+ if (EmulationSession::GetInstance().IsRunning()) {
+ EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
+ }
+ return static_cast<jboolean>(true);
+}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jint j_device,
jint j_button, jint action) {
if (EmulationSession::GetInstance().IsRunning()) {
+ // Ensure gamepad is connected
+ EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button,
action != 0);
}
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
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
jclass clazz);
+JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(
+ JNIEnv* env, jclass clazz);
+
+JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(
+ JNIEnv* env, jclass clazz, jstring j_device, jstring j_type);
+
+JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
+ JNIEnv* env, jclass clazz, jstring j_device);
+
+JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
+ JNIEnv* env, jclass clazz, jstring j_device);
+
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);