forked from MeloNX/MeloNX
add physical controller support
This commit is contained in:
parent
4f1bf2d0cc
commit
61ba5e7bff
@ -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)})
|
||||
|
||||
@ -88,7 +89,15 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
|
||||
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(){
|
||||
|
@ -132,7 +132,16 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
|
||||
|
||||
_nativeRyujinx.inputInitialize(width, height)
|
||||
|
||||
if(!settings.useVirtualController){
|
||||
controller.setVisible(false)
|
||||
}
|
||||
else{
|
||||
controller.connect()
|
||||
}
|
||||
|
||||
mainViewModel.activity.physicalControllerManager.connect()
|
||||
|
||||
//
|
||||
|
||||
_nativeRyujinx.graphicsRendererSetSize(
|
||||
surfaceHolder.surfaceFrame.width(),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user