android: Implement gamepad input

master
german77 2023-04-04 02:08:53 +07:00 committed by bunnei
parent f5c48f92f2
commit 6dfe4240ac
6 changed files with 510 additions and 11 deletions

@ -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,8 +188,9 @@ 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 =
.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() PreferenceManager.getDefaultSharedPreferences(applicationContext)
.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()
setControlScale(value.toInt()) setControlScale(value.toInt())

@ -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);