add physical controller support

This commit is contained in:
Emmanuel Hansen 2023-07-15 20:13:35 +00:00
parent 4f1bf2d0cc
commit 61ba5e7bff
7 changed files with 147 additions and 9 deletions

View File

@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.flowWithLifecycle
@ -45,6 +46,7 @@ typealias GamePad = RadialGamePad
typealias GamePadConfig = RadialGamePadConfig
class GameController(var activity: Activity, var ryujinxNative: RyujinxNative = RyujinxNative()) {
private var controllerView: View? = null
var leftGamePad: GamePad
var rightGamePad: GamePad
var controllerId: Int = -1
@ -65,7 +67,6 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
@Composable
fun Compose(lifecycleScope: LifecycleCoroutineScope, lifecycle:Lifecycle) : Unit
{
AndroidView(
modifier = Modifier.fillMaxSize(), factory = { context -> Create(context)})
@ -81,14 +82,22 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
}
}
private fun Create(context: Context) : View
private fun Create(context: Context) : View
{
var inflator = LayoutInflater.from(context);
var view = inflator.inflate(R.layout.game_layout, null)
view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(leftGamePad);
view.findViewById<FrameLayout>(R.id.rightcontainer)!!.addView(rightGamePad);
return view as View
controllerView = view
return controllerView as View
}
fun setVisible(isVisible: Boolean){
controllerView?.apply {
this.isVisible = isVisible
}
}
fun connect(){

View File

@ -132,7 +132,16 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
_nativeRyujinx.inputInitialize(width, height)
controller.connect()
if(!settings.useVirtualController){
controller.setVisible(false)
}
else{
controller.connect()
}
mainViewModel.activity.physicalControllerManager.connect()
//
_nativeRyujinx.graphicsRendererSetSize(
surfaceHolder.surfaceFrame.width(),

View File

@ -1,5 +1,6 @@
package org.ryujinx.android
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
@ -8,6 +9,8 @@ import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -29,6 +32,7 @@ import org.ryujinx.android.views.MainView
class MainActivity : ComponentActivity() {
var physicalControllerManager: PhysicalControllerManager
private var mainViewModel: MainViewModel? = null
private var _isInit: Boolean = false
var storageHelper: SimpleStorageHelper? = null
@ -41,6 +45,7 @@ class MainActivity : ComponentActivity() {
}
init {
physicalControllerManager = PhysicalControllerManager(this)
storageHelper = SimpleStorageHelper(this)
StorageHelper = storageHelper
}
@ -79,6 +84,21 @@ class MainActivity : ComponentActivity() {
}
}
@SuppressLint("RestrictedApi")
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
event?.apply {
return physicalControllerManager.onKeyEvent(this)
}
return super.dispatchKeyEvent(event)
}
override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
ev?.apply {
physicalControllerManager.onMotionEvent(this)
}
return super.dispatchGenericMotionEvent(ev)
}
private fun initialize() : Unit
{
if(_isInit)

View File

@ -0,0 +1,69 @@
package org.ryujinx.android
import android.view.KeyEvent
import android.view.MotionEvent
class PhysicalControllerManager(val activity: MainActivity) {
private var controllerId: Int = -1
private var ryujinxNative: RyujinxNative = RyujinxNative()
fun onKeyEvent(event: KeyEvent) : Boolean{
if(controllerId != -1) {
var id = GetGamePadButtonInputId(event.keyCode)
if(id != GamePadButtonInputId.None) {
when (event.action) {
KeyEvent.ACTION_UP -> {
ryujinxNative.inputSetButtonReleased(id.ordinal, controllerId)
}
KeyEvent.ACTION_DOWN -> {
ryujinxNative.inputSetButtonPressed(id.ordinal, controllerId)
}
}
return true;
}
}
return false
}
fun onMotionEvent(ev: MotionEvent) {
if(controllerId != -1) {
if(ev.action == MotionEvent.ACTION_MOVE) {
var leftStickX = ev.getAxisValue(MotionEvent.AXIS_X);
var leftStickY = ev.getAxisValue(MotionEvent.AXIS_Y);
var rightStickX = ev.getAxisValue(MotionEvent.AXIS_Z);
var rightStickY = ev.getAxisValue(MotionEvent.AXIS_RZ);
ryujinxNative.inputSetStickAxis(1, leftStickX, -leftStickY ,controllerId)
ryujinxNative.inputSetStickAxis(2, rightStickX, -rightStickY ,controllerId)
}
}
}
fun connect(){
controllerId = ryujinxNative.inputConnectGamepad(0)
}
fun GetGamePadButtonInputId(keycode: Int): GamePadButtonInputId {
return when (keycode) {
KeyEvent.KEYCODE_BUTTON_A -> GamePadButtonInputId.B
KeyEvent.KEYCODE_BUTTON_B -> GamePadButtonInputId.A
KeyEvent.KEYCODE_BUTTON_X -> GamePadButtonInputId.X
KeyEvent.KEYCODE_BUTTON_Y -> GamePadButtonInputId.Y
KeyEvent.KEYCODE_BUTTON_L1 -> GamePadButtonInputId.LeftShoulder
KeyEvent.KEYCODE_BUTTON_L2 -> GamePadButtonInputId.LeftTrigger
KeyEvent.KEYCODE_BUTTON_R1 -> GamePadButtonInputId.RightShoulder
KeyEvent.KEYCODE_BUTTON_R2 -> GamePadButtonInputId.RightTrigger
KeyEvent.KEYCODE_BUTTON_THUMBL -> GamePadButtonInputId.LeftStick
KeyEvent.KEYCODE_BUTTON_THUMBR -> GamePadButtonInputId.RightStick
KeyEvent.KEYCODE_DPAD_UP -> GamePadButtonInputId.DpadUp
KeyEvent.KEYCODE_DPAD_DOWN -> GamePadButtonInputId.DpadDown
KeyEvent.KEYCODE_DPAD_LEFT -> GamePadButtonInputId.DpadLeft
KeyEvent.KEYCODE_DPAD_RIGHT -> GamePadButtonInputId.DpadRight
KeyEvent.KEYCODE_BUTTON_START -> GamePadButtonInputId.Plus
KeyEvent.KEYCODE_BUTTON_SELECT -> GamePadButtonInputId.Minus
else -> GamePadButtonInputId.None
}
}
}

View File

@ -10,6 +10,7 @@ class QuickSettings(val activity: MainActivity) {
var enableDocked: Boolean
var enableVsync: Boolean
var useNce: Boolean
var useVirtualController: Boolean
var isHostMapped: Boolean
var enableShaderCache: Boolean
var enableTextureRecompression: Boolean
@ -27,5 +28,6 @@ class QuickSettings(val activity: MainActivity) {
enableShaderCache = sharedPref.getBoolean("enableShaderCache", true)
enableTextureRecompression = sharedPref.getBoolean("enableTextureRecompression", false)
resScale = sharedPref.getFloat("resScale", 1f)
useVirtualController = sharedPref.getBoolean("useVirtualController", true)
}
}

View File

@ -26,7 +26,8 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
ignoreMissingServices: MutableState<Boolean>,
enableShaderCache: MutableState<Boolean>,
enableTextureRecompression: MutableState<Boolean>,
resScale: MutableState<Float>
resScale: MutableState<Float>,
useVirtualController: MutableState<Boolean>
)
{
@ -39,6 +40,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
enableShaderCache.value = sharedPref.getBoolean("enableShaderCache", true)
enableTextureRecompression.value = sharedPref.getBoolean("enableTextureRecompression", false)
resScale.value = sharedPref.getFloat("resScale", 1f)
useVirtualController.value = sharedPref.getBoolean("useVirtualController", true)
}
fun save(
@ -50,7 +52,8 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
ignoreMissingServices: MutableState<Boolean>,
enableShaderCache: MutableState<Boolean>,
enableTextureRecompression: MutableState<Boolean>,
resScale: MutableState<Float>
resScale: MutableState<Float>,
useVirtualController: MutableState<Boolean>
){
var editor = sharedPref.edit()
@ -63,6 +66,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
editor.putBoolean("enableShaderCache", enableShaderCache?.value ?: true)
editor.putBoolean("enableTextureRecompression", enableTextureRecompression?.value ?: false)
editor.putFloat("resScale", resScale?.value ?: 1f)
editor.putBoolean("useVirtualController", useVirtualController?.value ?: true)
editor.apply()
}

View File

@ -92,6 +92,9 @@ class SettingViews {
var resScale = remember {
mutableStateOf(1f)
}
var useVirtualController = remember {
mutableStateOf(true)
}
if (!loaded.value) {
settingsViewModel.initializeState(
@ -100,7 +103,8 @@ class SettingViews {
enableVsync, enableDocked, enablePtc, ignoreMissingServices,
enableShaderCache,
enableTextureRecompression,
resScale
resScale,
useVirtualController
)
loaded.value = true
}
@ -121,7 +125,8 @@ class SettingViews {
ignoreMissingServices,
enableShaderCache,
enableTextureRecompression,
resScale
resScale,
useVirtualController
)
settingsViewModel.navController.popBackStack()
}) {
@ -136,7 +141,8 @@ class SettingViews {
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices,
enableShaderCache,
enableTextureRecompression,
resScale
resScale,
useVirtualController
)
}
ExpandableView(onCardArrowClick = { }, title = "System") {
@ -431,6 +437,25 @@ class SettingViews {
*/
}
}
ExpandableView(onCardArrowClick = { }, title = "Input") {
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Show virtual controller",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = useVirtualController.value, onCheckedChange = {
useVirtualController.value = !useVirtualController.value
})
}
}
}
}
}
}