android: Implement gamepad input
This commit is contained in:
parent
f5c48f92f2
commit
6dfe4240ac
|
@ -43,6 +43,21 @@ object NativeLibrary {
|
||||||
const val Player8Device = 7
|
const val Player8Device = 7
|
||||||
const val ConsoleDevice = 8
|
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
|
@JvmField
|
||||||
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
||||||
|
|
||||||
|
@ -70,6 +85,33 @@ object NativeLibrary {
|
||||||
} else getFileSize(appContext, path)
|
} else getFileSize(appContext, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Handles button press events for a gamepad.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,9 +8,9 @@ import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.*
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.View
|
import android.view.MotionEvent
|
||||||
import android.view.WindowManager
|
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.preference.PreferenceManager
|
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.fragments.EmulationFragment
|
||||||
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.ControllerMappingHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.NfcReader
|
import org.yuzu.yuzu_emu.utils.NfcReader
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
|
||||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
||||||
|
@ -38,6 +39,7 @@ open class EmulationActivity : AppCompatActivity() {
|
||||||
private var menuVisible = false
|
private var menuVisible = false
|
||||||
private var emulationFragment: EmulationFragment? = null
|
private var emulationFragment: EmulationFragment? = null
|
||||||
private lateinit var nfcReader: NfcReader
|
private lateinit var nfcReader: NfcReader
|
||||||
|
private lateinit var inputHandler: InputHandler
|
||||||
|
|
||||||
private lateinit var game: Game
|
private lateinit var game: Game
|
||||||
|
|
||||||
|
@ -80,6 +82,9 @@ open class EmulationActivity : AppCompatActivity() {
|
||||||
nfcReader = NfcReader(this)
|
nfcReader = NfcReader(this)
|
||||||
nfcReader.initialize()
|
nfcReader.initialize()
|
||||||
|
|
||||||
|
inputHandler = InputHandler()
|
||||||
|
inputHandler.initialize()
|
||||||
|
|
||||||
// Start a foreground service to prevent the app from getting killed in the background
|
// Start a foreground service to prevent the app from getting killed in the background
|
||||||
// TODO(bunnei): Disable notifications until we support app suspension.
|
// TODO(bunnei): Disable notifications until we support app suspension.
|
||||||
//foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
|
//foregroundService = new Intent(EmulationActivity.this, ForegroundService.class);
|
||||||
|
@ -108,6 +113,7 @@ open class EmulationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event)
|
return super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
nfcReader.startScanning()
|
nfcReader.startScanning()
|
||||||
|
@ -129,6 +135,29 @@ open class EmulationActivity : AppCompatActivity() {
|
||||||
super.onSaveInstanceState(outState)
|
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) {
|
private fun restoreState(savedInstanceState: Bundle) {
|
||||||
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
|
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
|
||||||
}
|
}
|
||||||
|
@ -159,7 +188,8 @@ open class EmulationActivity : AppCompatActivity() {
|
||||||
private fun adjustScale() {
|
private fun adjustScale() {
|
||||||
val sliderBinding = DialogSliderBinding.inflate(layoutInflater)
|
val sliderBinding = DialogSliderBinding.inflate(layoutInflater)
|
||||||
sliderBinding.slider.valueTo = 150F
|
sliderBinding.slider.valueTo = 150F
|
||||||
sliderBinding.slider.value = PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
sliderBinding.slider.value =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
||||||
.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
|
.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
|
||||||
sliderBinding.slider.addOnChangeListener(OnChangeListener { _, value, _ ->
|
sliderBinding.slider.addOnChangeListener(OnChangeListener { _, value, _ ->
|
||||||
sliderBinding.textValue.text = value.toString()
|
sliderBinding.textValue.text = value.toString()
|
||||||
|
|
|
@ -113,13 +113,15 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldUpdateView = false
|
var shouldUpdateView = false
|
||||||
|
val playerIndex =
|
||||||
|
if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
|
||||||
|
|
||||||
for (button in overlayButtons) {
|
for (button in overlayButtons) {
|
||||||
if (!button.updateStatus(event)) {
|
if (!button.updateStatus(event)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
NativeLibrary.onGamePadButtonEvent(
|
NativeLibrary.onGamePadButtonEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
button.buttonId,
|
button.buttonId,
|
||||||
button.status
|
button.status
|
||||||
)
|
)
|
||||||
|
@ -131,22 +133,22 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
NativeLibrary.onGamePadButtonEvent(
|
NativeLibrary.onGamePadButtonEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
dpad.upId,
|
dpad.upId,
|
||||||
dpad.upStatus
|
dpad.upStatus
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadButtonEvent(
|
NativeLibrary.onGamePadButtonEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
dpad.downId,
|
dpad.downId,
|
||||||
dpad.downStatus
|
dpad.downStatus
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadButtonEvent(
|
NativeLibrary.onGamePadButtonEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
dpad.leftId,
|
dpad.leftId,
|
||||||
dpad.leftStatus
|
dpad.leftStatus
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadButtonEvent(
|
NativeLibrary.onGamePadButtonEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
dpad.rightId,
|
dpad.rightId,
|
||||||
dpad.rightStatus
|
dpad.rightStatus
|
||||||
)
|
)
|
||||||
|
@ -159,13 +161,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
|
||||||
}
|
}
|
||||||
val axisID = joystick.joystickId
|
val axisID = joystick.joystickId
|
||||||
NativeLibrary.onGamePadJoystickEvent(
|
NativeLibrary.onGamePadJoystickEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
axisID,
|
axisID,
|
||||||
joystick.xAxis,
|
joystick.xAxis,
|
||||||
joystick.realYAxis
|
joystick.realYAxis
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadButtonEvent(
|
NativeLibrary.onGamePadButtonEvent(
|
||||||
NativeLibrary.Player1Device,
|
playerIndex,
|
||||||
joystick.buttonId,
|
joystick.buttonId,
|
||||||
joystick.buttonStatus
|
joystick.buttonStatus
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -38,6 +38,8 @@
|
||||||
#include "core/frontend/applets/software_keyboard.h"
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
#include "core/frontend/applets/web_browser.h"
|
#include "core/frontend/applets/web_browser.h"
|
||||||
#include "core/hid/hid_core.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/acc/profile_manager.h"
|
||||||
#include "core/hle/service/am/applet_ae.h"
|
#include "core/hle/service/am/applet_ae.h"
|
||||||
#include "core/hle/service/am/applet_oe.h"
|
#include "core/hle/service/am/applet_oe.h"
|
||||||
|
@ -274,6 +276,60 @@ public:
|
||||||
m_rom_metadata_cache.clear();
|
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() {
|
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() {
|
||||||
return m_software_keyboard;
|
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());
|
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,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env,
|
||||||
[[maybe_unused]] jclass clazz,
|
[[maybe_unused]] jclass clazz,
|
||||||
[[maybe_unused]] jint j_device,
|
[[maybe_unused]] jint j_device,
|
||||||
jint j_button, jint action) {
|
jint j_button, jint action) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
|
// Ensure gamepad is connected
|
||||||
|
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
||||||
EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button,
|
EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button,
|
||||||
action != 0);
|
action != 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
|
||||||
jclass clazz);
|
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(
|
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
|
||||||
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
|
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
|
||||||
|
|
||||||
|
|
Reference in New Issue